home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-04-13 | 92.7 KB | 3,428 lines |
- Newsgroups: comp.sources.unix
- From: jpr@jpr.com (Jean-Pierre Radley)
- Subject: v26i152: xc-4.1 - a serial communications program, V4.1, Part03/03
- Sender: unix-sources-moderator@vix.com
- Approved: paul@vix.com
-
- Submitted-By: jpr@jpr.com (Jean-Pierre Radley)
- Posting-Number: Volume 26, Issue 152
- Archive-Name: xc-4.1/part03
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 3 (of 3)."
- # Contents: xc.nro xcscrpt.c
- # Wrapped by vixie@gw.home.vix.com on Wed Apr 14 00:22:47 1993
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'xc.nro' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'xc.nro'\"
- else
- echo shar: Extracting \"'xc.nro'\" \(45748 characters\)
- sed "s/^X//" >'xc.nro' <<'END_OF_FILE'
- X.ds ]V version 4.1 JPRadley 10 April 1993
- X.tm
- X.tm xc.nro \*(]V
- X.tm
- X.tm If this isn't preprocessed by 'tbl' and postprocessed by 'col',
- X.tm well, my dear, really, why waste your time and mine?
- X.tm
- X.ta 4 4 4 4 4 4 4 4
- X.po 0
- X.nrLL 7.9i
- X.ll\n(LLu
- X.lt\n(LLu
- X.TH XC L
- X.SH Name
- xc \- communications program
- X.SH Syntax
- X.B xc
- X.RB [ -l device]
- X.RB [ -s script\ |
- X.BR -t ]
- X.SH Description
- X.B Xc
- calls out over a serial port to another computer. It can manage an interactive
- session or be called from
- X.BR cron (C).
- It has various means for transferring files between computers, and can be
- partially or totally under the control of scripts.
- X
- X.B Xc
- starts up by reading the file
- X.IR .xc ,
- which is sought for in this order: in "XC_PATH", in the current directory,
- in your
- X.I HOME
- directory, or in a default directory defined at compile time. This startup
- file may contain any of the valid
- X.I SET
- commands described below.
- X.B Xc
- then displays the current settings, and presents an
- X.I <XC>
- prompt, unless either the \fB-t\fR or \fB-s\fR option was present.
- X.SS \fIOptions\fR
- X.TP 9
- X.BI -l device
- Use the specified
- X.IR device ,
- which need not be the full pathname, e.g. "/dev/tty1A" or "tty1A" are
- equally acceptable.
- The line could either be directly connected to another computer, or have a
- modem on it. In either case (if a value was defined for DIDO at compile time),
- X.B xc
- attempts to suspend a
- X.BR getty (M)
- process that might be on that line, and then use the
- X.BR uucp (C)
- X.I LCK..file
- mechanism to lock the line.
- X.TP
- X.BI -s script
- XExecute the specified
- X.I script
- after program startup.
- X.TP
- X.B -t
- Go directly to terminal mode. The default is to go to command mode on program
- startup. This option is incompatible with the
- X.B -s
- option.
- X.SS \fIEnvironment\fR
- If the environment contains a variable called "MODEM", then the value
- of that variable will be the default device. As with the
- X.B -l
- flag, this need not be the
- full pathname. To set such a variable, in the Bourne shell, you might say:
- X.IP
- MODEM=tty01; export MODEM
- X.P
- or, in the C shell,
- X.IP
- setenv MODEM tty01
- X.P
- If the environment contains a variable called "BYEttyxx", where "ttyxx"
- stands for the basename of the modem line selected either by the MODEM
- variable or by the "-l" command option, then the value of that variable
- will be sent to the modem when the program terminates. This is useful
- for setting the modem back to some preselected state. Example:
- X.IP
- BYEtty01=ATZ ; export BYEtty01
- X.P
- Whatever string is contained in the "Byexxxxx" variable will be
- sent to the modem preceded and followed by a carriage-return.
- X
- Newer modems can be preset to revert to a predetermined state when the
- DTR signal of the computer is dropped, and so would not need to avail
- themselves of this feature. Furthermore,
- X.B getty
- programs which have been suspended, and which restart as
- X.B xc
- exits, will reset a modem using dialers or chat scripts referred to in the
- X/usr/lib/uucp/Devices file.
- X
- The environment may contain a variable called "XC_PATH", consisting
- of colon separated absolute paths to directories. If this variable is set to,
- say, "/usr/joe/xc:/usr/lib/xc", then those two directories are the
- first places searched for any scripts.
- X
- X.SS \fICommand Mode\fR
- When entering characters in command mode (that is, at the
- X.I <XC>
- prompt), control characters echo as ^x, where "x" is the control character that
- was entered. Backspacing (using whatever key is defined in the current
- environment) backspaces over both positions of the displayed control character,
- as expected.
- X
- An "interpreted" key can be added in a manner similar to that of
- X.BR vi (C);
- simply type ^V followed by the character to insert (backspace, delete,
- carriage return, or newline).
- X
- The following commands are available at the
- X.I <XC>
- command prompt:
- X
- X.TS
- tab(@);
- l1B lw(56) .
- c
- cis@T{
- Respond to an ENQ signal for a CIS B-Plus protocol transfer. This command is
- used for both uploading and downloading from CompuServe.
- T}
- X
- d
- dial@T{
- XEnter the dial directory. Returns to the \fI<XC>\fR
- prompt if exited without dialing, but returns to terminal mode after
- dialing or running a script.
- T}
- X
- s \fIfile
- script \fIfile\fR@T{
- XExecute \fIfile\fR, which contains appropriate \fBxc\fR
- script commands. Returns to terminal mode when the script is complete.
- T}
- X
- rb \fIfile\fR@(XMODEM binary receive)
- rt \fIfile\fR@T{
- X(XMODEM text receive) Receive \fIfile\fR from the remote system.
- T}
- X
- sb \fIfile\fR@(XMODEM binary send)
- st \fIfile\fR@T{
- X(XMODEM text send) Transmit \fIfile\fR to the remote system.
- T}
- X
- set \fR[\fIoptions\fR]@T{
- Display or set the transmission parameters used by \fBxc\fR. See below.
- T}
- X
- t
- term@Enter terminal mode.
- X
- x
- q
- exit
- quit@Exit program. Return to invoking program/shell.
- X
- h
- hangup@Hang-up the modem.
- X
- X%p \fIlocal\fR_\fIname\fR@T{
- X[\fIremote_name\fR] Transmit (put) a file to a remote Unix system. This command
- uses standard Unix utilities on the other end. \fIRemote_name\fR defaults to
- the same pathname as \fIlocal_name\fR if not otherwise specified.
- T}
- X
- X%t \fIremote\fR_\fIname\fR@T{
- X[\fIlocal_name\fR] Receive (take) a file from a remote Unix system. This
- command uses standard Unix utilities on the other end. \fILocal_name\fR
- defaults to the same pathname as \fIremote_name\fR if not otherwise specified.
- T}
- X
- X?
- help@T{
- Displays a brief summary of \fBxc\fR commands, the SET options, and
- terminal-escape commands.
- T}
- X.TE
- X.P
- X[Note: a compile-time option can disable the following three options to
- prevent any access to shells or other programs outside of
- X.BR xc .]
- X
- X.TS
- tab(@);
- l6B lw(56) .
- X! \fIcommand\fR@T{
- XExecute the specified \fIcommand\fR as a child process. If \fIcommand\fR is
- omitted, execute a local interactive shell.
- X(A space is required between the \fB!\fR and the \fIcommand\fR.)
- T}
- X
- X!!@Re-execute the last shell command string.
- X
- X$ \fIcommand\fR@T{
- XExecute a shell \fIcommand\fR with stdin and stdout redi\%rected to the
- modem port. This effectively puts the computer into a "host" mode.
- X(A space is required between the \fB$\fR and the \fIcommand\fR.)
- T}
- X.TE
- X.SS \fIUsing the SET Command\fR
- The
- X.I SET
- command is used to display and set/reset
- X.BR xc 's
- tunable parameters. Any of these commands may be placed in the
- X.I .xc
- file which is read upon starting up
- X.BR xc .
- X
- Used alone,
- X.I SET
- displays
- X.BR xc 's
- current parameters.
- X
- The following parameters can be set (note that except in the case of
- X.IR name s,
- these commands are case-\fIin\fRsensitive, and that there alternative forms
- of the commands shown on the XC help screen):
- X
- X.TS
- tab(@) ;
- l1 lw(54) .
- set auto on|off@T{
- Sets the auto-capture feature. When entering terminal mode, capturing
- commences without the necessity to manually request it.
- T}
- X
- set bps \fIvalue\fR
- set baud \fIvalue\fR@T{
- Set the desired bits/second rate. Supported bps rates are 300, 1200, 2400,
- X9600 (and, if included at compile time, 19200 and 38400).
- T}
- X
- set cfile \fIname\fR@Set the \fIname\fR of the capture file.
- X
- set cis on|off@T{
- Set response to a CompuServe file transfer request (<ENQ>). An "on" value
- specifies that when in terminal mode, an <ENQ> character will launch a CIS
- B-Plus protocol transfer. This parameter should be set "off" when not
- connecting to CompuServe, as phone line noise may cause a bogus file transfer
- request.
- T}
- X
- set cr on|off@T{
- In uploads using B-Plus protocol, setting this option "on" will insert a
- carriage-return after each newline in an ASCII file. If you expect your ASCII
- upload to be captured by DOS users, it is best to set cr "on". If your ASCII
- upload is meant strictly for Unix users, then you may set cr "off".
- The B-Plus protocol implementation used by \fBxc\fR
- will always strip end-of-line carriage-returns from incoming ASCII files.
- T}
- X
- set hdplx on|off@T{
- Sets the half-duplex flag. If this flag is set, then all characters typed at
- the keyboard while in terminal mode will be echoed to the screen as well as
- sent to the modem line. Useful for remote systems that don't echo characters.
- T}
- X
- set menu on|off@T{
- By default, a one-line menu is displayed above the \fI<XC>\fR prompt to
- remind the user of options most frequently chosen at this point:
- X"[d]ial directory [t]erminal mode [q]uit [s]cript [?]help".
- Setting the option "off" turns off this display.
- T}
- X
- set nl on|off@T{
- If this option is set "on", then newlines are sent as
- carriage-returns. If this option is set "off", then newlines are sent as
- newlines (carriage-returns are in any case always sent as carriage-returns).
- This option applies to input from the keyboard, or from a disk file using the
- X"\fBXCAPE\fR \fBF\fR" facility or a script "type" command (See below).
- This option has no effect on built-in XMODEM or B-Plus protocol transmissions,
- and has no effect on incoming data.
- T}
- X
- set pfile \fIname\fR@Set the \fIname\fR of the phone directory.
- X
- set proto \fIvalue\fR@T{
- This refers to the serial port character protocol, not to a file transfer
- protocol. The possible values are 8N1, 7E2, or 7O2, (case insensitive) which
- respectively set the modem port to a character size of 8 with no parity, or a
- character size of 7 with either even or odd parity. During B-Plus or XMODEM
- transfers, the port protocol is set to 8N, and reverts to its prior setting
- thereafter.
- T}
- X
- set xcape \fIchar\fR
- set escape \fIchar\fR@T{
- Set the \fBXCAPE\fR character (see below) to \fIchar\fR. This should be set to
- some non-printing character (other than backspace, tab, newline, of course).
- The character may be entered by depressing the Control key first
- and then typing a letter, or by typing a carat (^) followed by a letter.
- T}
- X
- set xon on|off
- set xoff on|off@T{
- Set XON/XOFF flow control flag. If "on", the program will honor the XOFF
- control character and wait until an XON character is received before
- transmitting any more information. If "off", the program will ignore
- XXOFF/XON requests.
- T}
- X.TE
- X.SS \fITerminal Mode\fR
- In terminal mode, all characters typed at the keyboard are sent to the
- modem, except that newline characters (0x0A) are translated to
- carriage-returns (0x0D) when you have "set nl 'on'"; all characters received
- from the modem are displayed on the local terminal screen.
- X
- X\fBXCAPE\fR is a key that will, when typed in terminal mode, introduce an
- X.B xc
- X"escape" command. Don't confuse \fBXCAPE\fR with the <ESCAPE> key.
- The default definition for \fBXCAPE\fR is ASCII 1, or Control-A. It can
- be redefined at run time with the "set escape \fIC\fR" command (or it can be
- redefined in the
- X.I .xc
- startup script).
- X
- When the \fBXCAPE\fR key is typed in terminal mode, the program will examine
- the next key pressed. If this key has been "bound" to perform a certain
- function, that function will be performed; otherwise, the second character is
- sent to the modem. Thus, to send the \fBXCAPE\fR character through the modem,
- it is necessary to press the key TWICE.
- X
- Thus the ESCAPE key itself would be a terrible choice for \fBXCAPE\fR, because
- it is used so often by other programs: if you were logged into a remote system
- and running, say, \fBvi\fR, it would be a great annoyance to have to hit ESCAPE
- twice to get it transmitted once.
- X
- The following keys (case \fIin\fRsensitive) are bound at startup time. Others
- may be added through the binding commands available in
- X.B xc
- scripts.
- X
- X.TS
- tab(@) ;
- l2 l2 l
- l2B l2 l .
- Command@Function@Description
- X
- XXCAPE /@Help@Display the table of bound keys
- XXCAPE ?@Help@Display the table of bound keys
- X
- XXCAPE b@\fIB\fRREAK@Send a MODEM BREAK signal (a sustained null).
- X
- XXCAPE d@\fID\fRirectory@Display the phone directory.
- X
- XXCAPE f@\fIF\fRile@Send a file through the modem (ASCII transfer).
- X
- XXCAPE s@\fIS\fRcript@\fBXc\fR will request the name of a script to execute.
- X
- XXCAPE h@\fIH\fRangup@Tell the modem to go on-hook.
- X
- XXCAPE y\fR@Capture \fIY\fRes@T{
- Open the capture file in APPEND mode (text received over the port accumulates
- at the end of the file). If the file is already open, a message is printed to
- that effect.
- T}
- X
- XXCAPE n\fR@Capture \fIN\fRo@T{
- Close the capture file. If it wasn't open, a message of regret is printed.
- T}
- X
- XXCAPE x\fR@e\fIX\fRit@Exit terminal mode back to \fI<XC>\fR command mode.
- X
- XXCAPE q@\fIQ\fRuit@Quit \fBxc\fR altogether.
- X.TE
- X.SS \fIPhonelist\fR
- X.B Xc
- looks for a
- X.I .phonelist
- file in the following order: in the current directory, in your home directory
- as defined by the $HOME environment variable, or in the LIBDIR as defined
- at compile time in
- X.I xc.h.
- However in command mode, any filename can be substituted, using the SET PFILE
- command, so long as it has the following characteristics:
- X
- It is ASCII text (lines of text separated by newlines).
- X
- The first field of data in each line (after any whitespace and up to the next
- occurence of whitespace) is a phone number in a valid format for
- the modem being used.
- X
- Descriptive text may follow the phone number. Spaces may be included in this
- text, but a <TAB> must terminate this text.
- X
- The following three definitions may then follow in any order:
- X.TP 12
- PROTO=sss
- X(sss=7e2|7o2|8n1) Set the serial port protocol for 7-bit characters with
- either even or odd parity and two stop bits, or for 8-bit characters with
- no parity and one stop bit.
- X.TP
- BPS=nnnn
- X(n=300|1200|2400|9600|19200|38400) Set the bits/second rate to the
- specified value.
- X.TP
- SCRIPT=file
- Immediately after sending the autodial string, execute the script file
- specified. (Note that the specified filename is CASE SENSITIVE!)
- X.P
- The following definition, if used, must be the LAST field on the line.
- X
- PREFIX=xxxxxxxx (e.g., PREFIX=ATs110=0s111=0)
- X
- The PREFIX="string" allows you send your modem a different setup string for
- each situation (default: no special setup). This is helpful for MNP and Telebit
- modems which require special attention for contacting non-MNP modems and other
- Telebits.
- X.SS \fIScripts\fR
- The
- X.B xc
- script language is intended to be closely similar to the Unix Bourne shell
- language. Unfortunately, it isn't identical to the Bourne shell, so it has
- the same problem that the
- X.BR awk (C)
- program does for those experienced in the C language: an
- X.B awk
- script LOOKS like C, but it isn't, really; and in the same way, an
- X.B xc
- script LOOKS like a Bourne shell script, but isn't. So the operation of the
- Bourne shell, if you're familiar with it, is useful as an analogy in
- understanding the
- X.B xc
- script language, but only as an analogy.
- X
- An
- X.B xc
- script consists of lists of commands. Commands are set off from each other
- within a list by either a newline ('\\n') or a semicolon (;), as is the case
- with the Bourne shell. The semicolon and the newline have identical effect as
- command separators, so (anticipating a bit here) you could say either
- X
- X.nf
- X if counter morethan 5
- X then
- X transmit "bye^M"
- X quit
- X endif
- X
- or
- X
- X if counter morethan 5; then transmit "bye^M"; quit; endif
- X
- X.fi
- and the result will be the same either way. The newline and the semicolon
- are treated the same way as in the Bourne shell, except that a newline cannot
- be quoted so as to remove its interpretation as a command terminator (in
- other words, a quoted string cannot contain a newline).
- X
- Commands are composed of
- X.IR word s
- separated by spaces or tab characters, and a
- X.I word
- can be one of the following:
- X.TP 3
- X*
- One of the
- X.B xc
- script language keywords, which are listed below, with appropriate explanations.
- X.TP
- X*
- A number, meaning a sequence consisting only of digits, with
- an optional leading minus sign to indicate a negative number.
- X.TP
- X*
- A literal string of characters surrounded by double-quotation marks ('"'); such
- a string can be no longer than 80 characters. A double-quotation mark can be
- imbedded within the string by preceding it with a backslash ('\\"'); when the
- string is interpreted, the backslash is disregarded and the double-quotation
- mark is treated as part of the string. Mismatched quotation marks result in a
- syntax error.
- X.TP
- X*
- The name of a user variable, which can have either a string or a numeric value.
- The name of a user variable must begin with an alphabetic character.
- X.TP
- X*
- The name of a shell environment variable, preceded by a dollar sign ('$'). The
- X.B xc
- script language examines the Unix environment for the specified variable and,
- if such a variable exists, substitutes the value of that variable. The result
- is treated as if it were a quoted literal string, and, therefore, should not be
- more than 80 charac\%ters long, or else it will be truncated to its first 80
- characters. Similarly, such an environment variable should not contain a
- newline.
- X.TP
- X*
- An expression surrounded by back-quotation marks ('`'). This sort of expression
- operates similarly to the Bourne shell's command-substi\%tution mechanism: the
- contents of the back-quotes are passed to the Bourne shell, and the standard
- output of the back-quoted command is treated as if it were a quoted literal
- string. Therefore, the command's output should not be more than 80 characters
- long, nor contain a newline. Also, the contents of the back-quotes cannot be
- longer than 80 characters, nor contain a newline.
- X.TP
- X*
- An expression introduced by a pound-sign (or number-sign: '#'), which is
- treated as a comment. All characters from the '#' until the end of the
- physical line are ignored. This comment mechanism is the same as in the
- Bourne shell.
- X.TP
- X*
- A limitation of the script language is that only one character string can be
- passed as an argument to any of the script commands. The \fIbind_function\fR,
- X\fIbind_script\fR, and \fIbind_string\fR commands thus need to use a decimal
- digit to represent the key that is to be bound. The ASCII value of the the key
- is employed. As an example, Control-C is identified as 3, Control-Z is 26, the
- XESCAPE key is 27, a space is 32, the number "1" is 49; letters are
- X.RI case- in sensitive
- so that identifying the bound key as "65" or as "97" will always bind
- X.I both
- X"a" and "A" to the same command.
- X.P
- Quoted literal strings (and the two other mechanisms that act like quoted
- literal strings, shell environment variables and back-quoted shell commands)
- may be up to 80 characters long. All other words must be no longer than 16
- characters, and are treated case-independently (which is to say, uppercase
- is the same as lowercase); note, though, that the names of shell environment
- variables are case-dependent (uppercase must match uppercase and lowercase
- must match lowercase), because they are case-dependent in the shell.
- X
- Any word not recognizable within the foregoing categories is treated as the
- name of a new user variable. Such a word, if longer than 16 characters, is
- considered to be a syntax error.
- X
- User variables are created with the 'assign' script keyword, and may have
- either numeric or string values. The type of a user variable is determined
- by how it's created; if it's assigned to a string, it's a string variable,
- and if it's assigned to a number, it's a numeric variable. The value of any
- user variable can be changed with another 'assign' command, and numeric
- variables can be changed to string variables and vice-versa. Shell
- environment variables cannot be changed within an
- X.B xc
- script, but the value of a shell environment variable can be assigned to a user
- variable, and the value of the user variable can thereafter be changed.
- X
- Scripts are contained in ASCII text disk files, one script to a file. A
- script can invoke another script as a subroutine with the 'call' keyword; up
- to 5 scripts can be nested in this way at any single time.
- X
- With all this said, the following list of
- X.B xc
- script language commands should be comprehensible. The format "<something>"
- means that a token, or word-type, of the "something" type is meant rather than
- the literal sequence 'something'.
- X.SS \fIScript Language Commands\fR
- Note that all the commands are case-\fIin\fRsensitive!
- X.TP 4
- X.I affirm
- Syntax: affirm
- X
- Reads a string from the terminal, and returns TRUE if the string begins with
- X\&'y' or 'Y'; otherwise, returns FALSE. Used in evaluating conditional
- expressions. The string must be terminated by a newline or carriage-return.
- X
- XExample:
- X
- X echo -n "Continue (y/n)? "
- X if affirm
- X then
- X continue
- X else
- X break
- X endif
- X.TP
- X.I assign
- Syntax: assign <varname> eq <number>
- X assign <varname> eq "string"
- X assign <varname1> eq <varname2>
- X assign <varname> eq <script-command>
- X
- Assigns to user variable <varname> the value following "eq"; if that value is a
- number, then <varname> becomes a numeric user variable; if that value is a
- string, then <varname> becomes a string user variable. If <varname> does not
- already exist as a user variable, it is created. Variable space is allocated
- dynamically, but running out of memory space for variables is unlikely. All
- variables are global across scripts that run at the same time via the 'call'
- keyword, and all variables vanish when a script, called directly from
- X.B xc
- as opposed to called from another script, exits. In other words, variable
- values are not static except during 'call' execution. Variable names cannot be
- longer than 8 characters. Successive 'assigns' are permissible, and the type of
- the variable changes according to the type of the value following "eq". A user
- variable is destroyed with the 'unassign' keyword.
- X
- If a variable is assigned the value of a script command, then it becomes a
- numeric variable with value TRUE or FALSE, depending on the status returned by
- the script command. If a variable is assigned the value of a back-quoted
- command, it becomes a string variable with the value of the first 80 characters
- of the back-quoted command. If a variable is assigned equal to an environment
- variable, it becomes a string variable with the value of the first 80
- characters of the value of the environment variable.
- X
- XExamples:
- X
- X assign numvar eq 5
- X assign strvar eq "This variable is a string"
- X assign mydir eq $HOME
- X assign numvar2 eq numvar
- X assign strvar2 eq strvar
- X assign numvar eq true
- X assign today eq `date`; echo "today is " today
- X.TP
- X.I beep
- Syntax: beep
- X
- Sends a Control-G to the terminal. Useful for alerting the user that some event
- has occurred, for example a CONNECT after a lengthy redialing session.
- X.TP
- X.I bind_function
- Syntax: bind_function \fIcode\fR "function"
- X
- Bind the character identified by the number specified by \fIcode\fR to the
- X\fBxc\fR builtin function "function".
- X
- The "function" may be one of the following (case is ignored):
- X
- X BRKCHAR Send modem BREAK signal
- X CAPTEND Turn off terminal mode capture
- X CAPTYES Turn on terminal mode capture
- X DIALCHR Dial from phonelist
- X DIVCHAR Send file through modem
- X DOSCRPT Execute script file (prompts interactively)
- X EMITSTR Emit string
- X ENDCHAR Exit terminal mode
- X HLPCHAR Display terminal mode key bindings
- X HUPCHAR Hang up modem
- X QUITCHR Quit program
- X SCRPCHR Prompt for script file
- X
- XExample:
- X
- X bind_function 26 "quitchr"
- X
- This binds Control-Z to quit the XC program.
- X.TP
- X.I bind_script
- Syntax: bind_script \fIcode\fR "scriptname"
- X
- Bind the character identified by the number specified by \fIcode\fR to execute
- the script named by "scriptname".
- X
- Upon termination of the script, the program will resume terminal mode, unless
- the "quit" script function was executed.
- X
- XExamples:
- X
- X bind_script 18 "/usr/lib/xc/.rz"
- X
- This binds Control-R to execute the script /usr/lib/xc/.rz. The .rz script
- supplied with the source code contains:
- X
- X tty "on"
- X echo -n "What files are to be received? "
- X read FILES
- X transmit "sz -y "
- X transmit FILES
- X transmit "^M"
- X echo "Starting ZMODEM Receive (rz -y)"
- X pipe "rz -y"
- X
- Pressing \fBXCAPE\fR \fB^R\fR will ask for some file name(s), and start an
- X.B sz
- command on the remote system, and an
- X.B rz
- on the local system to receive the file(s).
- X
- X bind_script 19 "/usr/lib/xc/.sz"
- X
- This binds Control-S to execute the script /usr/lib/xc/.sz. The .sz script
- supplied with the source code contains:
- X
- X tty "on"
- X echo -n "What files are to be sent? "
- X read FILES
- X echo "Starting ZMODEM send (sz -y " FILES ")"
- X pipe "sz -y " FILES
- X
- Pressing \fBXCAPE\fR \fB^S\fR will ask for some file name(s), and then launch
- X.B sz
- on the current system.
- X.B Sz
- will itself start an
- X.B rz
- process on the remote system.
- X
- X.TP
- X.I bind_string
- Syntax: bind_string \fIcode\fR "string"
- X
- Bind the character identified by the number specified by \fIcode\fR to emit the
- string specified by "string".
- X
- The string is sent to the modem untranslated; eg, it is not examined for
- embedded terminal mode escapes.
- X
- XExample:
- X
- X bind_string 49 "AAATZ^M"
- X
- This binds "1" to send the sequence to reset the modem.
- X.TP
- X.I break
- Syntax: break
- X
- XExits from the immediately enclosing 'while' loop. Identical to the C language
- X\&'break', and to the Bourne shell 'break' except that the
- X.B xc
- script language 'break' does not take a numeric argument. Don't confuse this
- with the script keyword 'xmitbrk', which sends a BREAK signal to the modem port.
- X.TP
- X.I call
- Syntax: call "scriptname"
- X
- Suspends execution of the current script, and attempts to load and run the
- specified scriptname. The scriptname must be a quoted literal string. There is
- no
- X.B xc
- analogue of the Bourne shell "exec" command; all subscripts in
- X.B xc
- are treated as sub-routines. All variables are global across subscripts, so if
- a subscript changes the value of a variable, then that change will remain in
- effect after return to the parent script. Shell environment variables cannot be
- changed by any
- X.B xc
- script.
- X.TP
- X.I capture
- Syntax: capture "on"
- X capture "off"
- X
- Turns the file-capture function on or off. Note that the arguments must be
- quoted literal strings. Note also that when the script exits into terminal
- mode, the file-capture function is turned off. If you have 'set auto "on"',
- then you will begin capturing immediately upon entering, or returning to,
- terminal mode. If not, then you must manually type "\fBXCAPE\fR \fBY\fR" to
- start capturing when entering terminal mode.
- X.TP
- X.I continue
- Syntax: continue
- X
- Resumes execution at the top of the immediately enclosing 'while' loop.
- Identical to the C language 'continue' instruction, and to the Bourne shell
- X\&'continue' command except that no numeric argument is accepted.
- X.TP
- X.I debug
- Syntax: debug "on"
- X debug "off"
- X
- If the 'debug' option is on, then
- X.B xc
- will make many parenthetical comments about what it's doing while it runs the
- script. These comments can sometimes be helpful in debugging script logic. Note
- that the argument must be a quoted literal string.
- X
- While debugging a script containing a password, turn this option off, then on
- again, to reduce the excitement-level of any colleagues collected circa your
- screen.
- X.TP
- X.I decr
- Syntax: decr <numeric-variable>
- X
- Decrements the value of the specified numeric user variable by 1. Useful in
- controlling loop execution. If the specified variable isn't numeric, or doesn't
- exist, a syntax error results.
- X.TP
- X.I dial
- Syntax: dial "number-string"
- X
- Dials the specified number, using modem-command strings defined in xc.h.
- X.TP
- X.I echo
- Syntax: echo "string" <variable> ...
- X echo -n "string" <variable> ...
- X
- This command sends its arguments to the user's terminal. The number of
- arguments is optional, except that the total result may not exceed 80
- characters. Variables and back-quoted shell commands are expanded as necessary.
- X
- If the "-n" switch is present, then no carriage-return/newline sequence is
- appended to the output.
- X
- XExamples:
- X
- X echo "The time and date are now " `date`
- X echo "My terminal type is " $TERM
- X echo "My terminal type is " $TERM " today."
- X
- Note that whitespace isn't echoed unless it's part of a quoted literal string.
- X.TP
- X.I exit
- Syntax: exit
- X
- Terminates execution of the current script. If a script reaches its end, it
- exits automatically, so 'exit' is useful mainly to terminate a script
- prematurely.
- X.TP
- X.I false
- Syntax: false
- X
- Same as the Unix 'false' command. Does nothing, but returns a FALSE status
- value. Useful within conditional expressions.
- X
- XExample:
- X
- X if waitfor "CONNECT" 30 eq false
- X then
- X quit
- X endif
- X
- Note that above example could be rewritten using the negating modifier "!":
- X
- X if ! waitfor "CONNECT" 30
- X then
- X quit
- X endif
- X
- and note too that the "!" must be separated from its argument by whitespace.
- X.TP
- X.I file
- Syntax: file <script-command>
- X
- The standard output of the specified script command is sent to the current
- capture file. If the "capture" option is not set, then an error message is
- displayed, but script execution continues.
- X
- XExamples:
- X
- X file echo "--------- CUT HERE ----------"
- X
- Sends the output of the 'echo' command to the current capture file, provided
- that the "capture" option is now "on".
- X
- X file echo `date`
- X
- Sends a timestamp to the current capture file, provided that the "capture"
- option is now "on". The same thing could have been done with
- X
- X file shell "date"
- X
- X.TP
- X.I hangup
- Syntax: hangup
- X
- Tells the modem to go on-hook.
- X
- X.TP
- X.I if\ \
- Syntax: if <list1>; then <list2>; [ else <list3>; ] endif
- X
- If <list1> evaluates as TRUE, performs <list2>; otherwise, if <list3> is
- specified, performs <list3>; then resumes execution immediately following
- X\&'endif'. To accommodate those whose minds wander while writing scripts, 'fi'
- is an acceptable synonym for 'endif'.
- X
- XEach list may consist of any number of script commands separated by semicolons
- or newlines. The value of <list1> is determined by inclusively OR'ing the value
- of each directive in the list, so that if any of the directives in <list1>
- evaluates as TRUE, then so will <list1>. <list1> is performed in its entirety
- regardless of the value of any of its component commands.
- X
- The keywords 'then', 'else', and 'endif' (or 'fi') must be immediately preceded
- by command separators, either a semicolon or a newline, just as is the case in
- the Bourne shell.
- X
- XFor conditional evaluation in 'if' and 'while' constructions, the following
- comparators are available in addition to the script directives mentioned
- elsewhere:
- X
- X <varname1> eq "string"
- X <varname1> eq <number>
- X <varname1> eq <varname2>
- X <varname1>
- X "string"
- X
- evaluates as TRUE if the value of user variable <varname1> is the same as that
- of a specified string or numeric constant or of a specified second variable
- name. If the variable name <varname1> is not followed by anything else, then
- the expression evaluates as TRUE if the variable is numeric and has a non-zero
- value, or if the variable is a string variable and has a non-zero length;
- otherwise, the expression evaluates as FALSE. Comparing a string variable to a
- numeric variable, or vice-versa, causes a syntax error.
- X
- If a conditional expression consists only of a quoted literal string, the
- expression evaluates as TRUE if the string's length is non-zero, and otherwise
- evaluates to FALSE. Because environment variables and back-quoted shell
- commands are treated as if their output/value were quoted literal strings, this
- allows direct testing of a shell command or of an environment for non-zero
- length. Nonexistent environment variables are treated as if they exist with the
- value "" (a string of zero length).
- X
- X <varname1> neq "string"
- X <varname1> neq <number>
- X <varname1> neq <varname2>
- X
- evaluates as TRUE if the value of user variable <varname1> is not equal to that
- of a specified string or numeric constant or of a specified second variable
- name. Comparing a string variable to a numeric variable, or vice-versa, causes
- a syntax error.
- X
- X <varname1> lessthan "string"
- X <varname1> lessthan <number>
- X <varname1> lessthan <varname2>
- X
- evaluates as TRUE if the value of user variable <varname1> is less than that of
- a specified string or numeric constant or of a specified second variable name.
- String variables are compared lexically according to ASCII value.
- X
- X <varname1> morethan "string"
- X <varname1> morethan <number>
- X <varname1> morethan <varname2>
- X
- operates identically to 'lessthan', except in reverse.
- X
- The value of any conditional expression may be negated by preceding it with an
- exclamation point followed by a space or tab.
- X
- XExamples:
- X
- X if counter eq 0; then break; endif;
- X if var1 eq var2; then echo "identical"; endif
- X if counter morethan 20; then break; endif;
- X if counter lessthan 0; then break; endif;
- X if ! counter; then echo "counter is " counter; endif
- X
- To perform a list if any of a set of conditions exist:
- X
- X if counter morethan 5;
- X counter eq brkvalue; # a second comparator
- X then break;
- X endif;
- X
- i.e., perform the 'break' directive if the value of numeric user
- variable 'counter' is greater than the numeric constant 5, or if the value
- of 'counter' is equal to that of the user numeric variable 'brkvalue'.
- X.TP
- X.I incr
- Syntax: incr <numeric-variable>
- X
- Increments the value of the specified numeric user variable by 1. The
- opposite of 'decr'.
- X.TP
- X.I linked
- Syntax: linked
- X
- X\&'linked' is a pseudo-function that evaluates as TRUE if the current script has
- been invoked from the dialing directory, and as FALSE otherwise.
- X
- XExample:
- X
- X if ! linked; then
- X dial "5551212"
- X endif
- X.TP
- X.I pause
- Syntax: pause <number>
- X
- Suspends execution for the specified <number> of SECONDS. Identical to Unix
- X"sleep."
- X.TP
- X.I pipe
- Syntax: pipe "<shell-command>"
- X
- The standard input and standard output of the specified shell command are
- connected to the modem port, and the command is executed. This is the
- equivalent of the command mode "$" command.
- X
- XExamples:
- X
- X pipe "echo \\"\\177\\""
- X
- sends a DELETE character to the modem.
- X
- X pipe "rz"
- X
- performs a file receive via ZMODEM, assuming that Chuck Forsberg's 'rz/sz'
- programs reside on your system.
- X
- X.TP
- X.I portname
- Syntax: portname
- X
- This isn't a command, but a synonym for the current modem terminal device,
- treated as if it were a quoted string. Useful if modems of differing types are
- attached to different terminal lines and need different dialing or
- initialization sequences. To compare the value of 'portname' to some other
- string, you must first assign a user variable equal to 'portname'.
- X
- XExamples:
- X
- X assign myport eq portname
- X if myport eq "/dev/tty01"
- X then
- X transmit "AT&E0^M"
- X endif
- X.TP
- X.I quit
- Syntax: quit
- X
- XExits the script and terminates
- X.B xc
- entirely. Any LCKfile will be removed if possible and the
- user's terminal will be restored to the state it was in when
- X.B xc
- was invoked.
- X.TP
- X.I read
- Syntax: read <variable-name>
- X
- Takes a string from the user's keyboard and places it into the specified user
- variable. Any previous value of the specified variable is discarded.
- X.TP
- X.I redial
- Syntax: redial
- X
- Redials the number most recently dialed with the 'dial' command.
- X.TP
- X.I seen
- Syntax: seen "string" <number>
- X
- XEvaluates as TRUE if "string" has occurred within the last <number> characters
- received during the most recent 'waitfor' command. Only up to 2048 characters
- are remembered at any one time during 'waitfor' processing. If no <number> is
- specified, then all the characters received during the most recent 'waitfor'
- command are examined, up to a maximum of 2048. The 'seen' buffer is reset at
- the beginning of each 'waitfor' command. This is useful to tell which of
- several strings has been received.
- X
- XExample:
- X
- X if waitfor "string1" 20
- X then
- X echo "Received 'string1'."
- X else
- X if seen "string2"
- X then
- X echo "Received 'string2' instead."
- X endif
- X endif
- X.TP
- X.I set\
- Syntax: set
- X.RB < xc -set-option>
- X<value>
- X
- This command is the same as the command-mode
- X.B xc
- X\&'set' command, such as "set bps 1200", "set cis on", and so forth, except that
- a string <value> must be enclosed within double-quotation marks.
- X
- XExamples:
- X
- X set cis "on"
- X set cfile "newfilename"
- X set auto "on"
- X set bps 2400
- X.TP
- X.I shell
- Syntax: shell "<shell-command>"
- X
- The shell command enclosed within the double-quotation marks is executed. This
- is similar to the
- X.B xc
- command-mode "!" command. Remember that if the shell command contains
- double-quotation marks, they must be escaped with backslashes.
- X.TP
- X.I timeout
- Syntax: timeout <number>
- X
- If <number> is greater than zero, starts a timer which will cause the MOST
- DEEPLY NESTED script to exit when <number> of MINUTES expire. If <number> is
- zero, then any pending timeout is cancelled. If <number> is negative, nothing
- happens.
- X
- XExpiration of the specified timeout causes the most deeply nested script to
- exit, not to terminate
- X.BR xc .
- To cause the program to quit if a timeout expires, use a subscript.
- X
- XExample:
- X
- X 'script1' contains:
- X
- X call "script2"
- X if expired
- X then
- X quit
- X endif
- X # more commands
- X
- X
- X 'script2' contains:
- X
- X assign expired eq 1
- X timeout 5 # limit of 5 minutes
- X while ! waitfor "login:" 30
- X do
- X xmitbrk
- X done
- X assign expired eq 0
- X exit
- X
- When 'script2' exits, the numeric variable 'expired' will be set to 1 if
- X\&'script2' timed out, and will be 0 otherwise. 'script1' can act on this
- information accordingly.
- X.TP
- X.I transmit
- Syntax: transmit "string"
- X
- Sends a string to the modem. The string is sent with brief pauses between
- characters, to accommodate modems that cannot accept rapid command input, and
- within the string, any alphabetic character preceded by a caret (^) is
- translated to the corresponding Control-character.
- X
- XExample:
- X
- X transmit "ATDT 5551212^M"
- X
- sends the string "ATDT 5551212" to the modem, followed by a carriage-return
- character.
- X.TP
- X.I true
- Syntax: true
- X
- Does nothing, but always evaluates as TRUE. Useful in conditional
- expressions. The opposite of 'false'.
- X.TP
- X.I tty\
- Syntax: tty "on"
- X tty "off"
- X
- Ordinarily, during script execution, characters received from the modem port
- are echoed to the user's terminal screen. This happens only during 'waitfor'
- and 'type' execution, so it may be a bit choppy. This echoing can be turned
- off with
- X
- X tty "off"
- X
- and turned back on with
- X
- X tty "on"
- X
- Note that "on" and "off" must be enclosed in quotation marks.
- X.TP
- X.I type
- Syntax: type "<filename>"
- X
- Sends the specified ASCII file to the modem port. This is the same as the
- X.B xc
- terminal-mode "send ASCII file" escape.
- X.TP
- X.I unassign
- Syntax: unassign <variablename>
- X
- XErases the specified user variable. The variable may be either numeric or
- string type. The variable name must not be enclosed in quotation marks, because
- variable names are considered to be
- X.B xc
- script keywords, and not literal strings.
- X.TP
- X.I waitfor
- Syntax: waitfor "string" <number>
- X
- Scans input from the modem port for an occurrence of the specified string,
- which must be enclosed in quotation marks. The scanning continues for the
- specified <number> of SECONDS or until the specified string is identified in
- the modem input stream, whichever comes first. This command evaluates as TRUE
- if the specified string is found, and as FALSE if the specified <number> of
- SECONDS elapses and the string isn't found within that time. The default time,
- if no <number> is specified, is roughly 30 seconds.
- X
- String matching is performed on a case-\fIin\fRsensitive basis.
- Within the string, any alphabetic character preceded by a caret (^) is
- translated to the corresponding Control-character.
- X
- XExamples:
- X
- X assign counter eq 1
- X while ! waitfor "login:" 15
- X do
- X xmitbrk; incr counter; if counter morethan 5
- X then
- X quit
- X endif
- X done
- X
- If in a CompuServe Forum the "prompt character" has been set by the user to
- be a backspace, this test will log off if the main prompt is not seen in the
- next sixty seconds:
- X
- X if ! waitfor "forum !^H" 60
- X then
- X transmit "bye^M"; quit
- X endif
- X
- If the 'cis' option has been set to "on", either in the
- X.I .xc
- startup script, or by a direct 'set' command from command mode, or with
- X
- X set cis "on"
- X
- within a current script, and if during 'waitfor' processing a CIS B-Plus
- protocol file transfer request is received, then the B-Plus protocol transfer
- is performed, the <number> argument is reset to its original value, and
- X\&'waitfor' processing continues. This allows automatic B-Plus protocol file
- transfers from within a script.
- X.TP
- X.I while
- Syntax: while <list1>; do <list2>; done
- X
- Operates similarly to the 'if' command, except that <list2> is executed
- repeatedly so long as <list1> evaluates as TRUE. All the conditional
- comparators and rules for comparisons that apply for the 'if' command also
- apply to 'while'. (Note that 'while' loops can be nested within 'if' commands
- and vice-versa.)
- X.TP
- X.I xmitbrk
- Syntax: xmitbrk
- X
- Sends a BREAK signal to the modem port.
- X.SS \fIFile Transfers\fR
- When transferring files using the XMODEM protocol, the file mode is specified
- in the upload/download command. A TEXT mode transfer enables translation of the
- transmitted or received file to support CP/M and MS-DOS end-of-line characters.
- When transmitting a file using TEXT mode, all newlines are converted to
- carriage-return/newline sequences. When receiving a file using TEXT mode, all
- carriage-return/newline sequences are converted to just a newline. A BINARY mode
- transfer transmits the file "as is" without any such conversion.
- X
- When transferring files using CompuServe B-Plus protocol, the format of the
- file is specified by the host. An ASCII mode will force
- X.B xc
- to perform TEXT mode translation; a BINARY mode will not do any translation.
- This means that, in either direction, a BINARY mode will send bytes "as is",
- whereas in ASCII mode, an incoming file will always be stripped of end-of-line
- carriage-returns, while an ASCII file being uploaded may or may not have
- carriage-returns added after each newline, depending on the "on" or "off"
- setting of the "cr" option.
- X
- When using either the "%t" (take) command, an XMODEM receive command, or a
- CompuServe B-Plus download command,
- X.B xc
- will check if the file name you specify already exists on your system, and ask
- for an OK to overwrite the file, or for an alternative name. In the CompuServe
- case, there will also be an option to "Resume" a download. If the CRC checksum
- of the bytes that you already have in the file matches CompuServe's checksum on
- the the same initial bytes in its version of the file, file transfer will
- proceed from that point onwards. This saves connect time when a very large
- download has been interrupted during a prior session.
- X
- Script-driven file transfers using the CompuServe B-Plus protocol are more or
- less built into the
- X.B xc
- script language, since during 'waitfor' processing, a file transfer request
- from the modem port will trigger a B-Plus transfer if the "cis" mode is set.
- The difficulty in performing such transfers isn't in the transfer itself, but
- rather in the maneuvering required to get into position to transfer the correct
- file, and is something that probably only experienced script writers should
- wrestle with. However, if we assume that in the midst of a script, you've
- reached a point where you can issue a "download" command to CompuServe, for
- instance a "Disposition" prompt during a File Library "BROWSE" command, and you
- want to download the present file, which is "XCALL.C" on CompuServe, and
- X"xcall.c" does not exist in your current working directory, you'd use the
- following sequence of script commands to do it:
- X
- X set cis on # can't do auto file B-Plus transfer otherwise
- X transmit "dow^M"
- X pause 2 # wait for CIS to display protocol menu
- X transmit "6^M" # B-Plus is number 6 on that menu
- X transmit "xcall.c^M" # "file name for your computer:"
- X waitfor "Disposition"
- X
- During the final "waitfor" processing, the CompuServe ENQ character will
- be recognized and the transfer will proceed automatically. Then 'waitfor'
- will continue waiting for the "Disposition" prompt, after which your script
- can proceed. The same sort of thing can be done with file uploads, but once
- again, the difficulty isn't with the transfer, but rather with setting things
- up so that the transfer will be requested at the correct place.
- X
- A shell script,
- X.B cisdownload
- provided with the distribution, can automatically retrieve a single file from a
- CompuServe library.
- X
- XFor XMODEM, YMODEM, and ZMODEM transfers via scripts, there's no mechanism
- built into the
- X.B xc
- script language to use the built-in, 128-byte-packet XMODEM in
- X.BR xc .
- Instead, we strongly recommend that you obtain Chuck Forsberg's excellent,
- shareware utility called "RZSZ", which can handle XMODEM, XMODEM-1K,
- YMODEM, and ZMODEM transfers and which can be used from within
- X.B xc
- with the 'pipe' command, or from command mode with the '$' command, or using
- the examples under the
- X.I bind_script
- command above.
- X.SH Exit Codes
- X.TS
- tab(@) ;
- c2B lw(60) .
- X0@Successful, uneventful completion.
- X
- X1@Error in command-line arguments.
- X
- X2@Failure in forking to execute a command or run a shell.
- X
- X3@T{
- No modem port specified on the command line, or contained in the environment
- variable MODEM.
- T}
- X
- X4@Inability to create a LCK..file for the specified port.
- X
- X5@Inability to open the specified port.
- X
- X6@No environment variable TERM has been set.
- X
- X7@No entry for the current TERM setting in /etc/termcap.
- X
- X8@Problem caused by the 'ungetty' program.
- X.TE
- X.SH Ccpyright
- X.B Xc
- and its source files and sample scripts and manual page are Copyright 1993 by
- Jean-Pierre Radley.
- X
- Permission is granted to the public to use this code in any manner, without
- any warranty, implied or otherwise, of fitness for a particular purpose.
- X
- By virtue of a restriction previously placed upon all code derivative from
- X.BR xcomm ", the " xc
- code and associated files may not be sold by anyone to anyone, nor incorporated
- into any product that is not also free. It's OK to transfer them for free.
- X.SH Authors
- This manual page was written by Fred Buck (1989) and Jean\-Pierre Radley
- X(1990, 1991, 1992, 1993).
- X.B Xc
- itself is the product of many synergistic wise minds. See the README document.
- X.SH Version
- This edition of the manual is for XC \*(]V.
- END_OF_FILE
- if test 45748 -ne `wc -c <'xc.nro'`; then
- echo shar: \"'xc.nro'\" unpacked with wrong size!
- fi
- # end of 'xc.nro'
- fi
- if test -f 'xcscrpt.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'xcscrpt.c'\"
- else
- echo shar: Extracting \"'xcscrpt.c'\" \(44712 characters\)
- sed "s/^X//" >'xcscrpt.c' <<'END_OF_FILE'
- X/* xcscrpt.c -- script interpreter module for XC");
- X This file uses 4-character tabstops
- X Author: larry gensch, December 1987
- X Major rewrite: fred buck, Jan 1989
- X Binding code: larry gensch, September 1991
- X This code is released to the public domain
- X*/
- X
- X#include <stdio.h>
- X#include <string.h>
- X#include <sys/types.h>
- X#include <sys/times.h>
- X#include <sys/param.h>
- X#include <ctype.h>
- X#include <signal.h>
- X#include <setjmp.h>
- X#include <sys/stat.h>
- X#include "xc.h"
- X
- jmp_buf here;
- extern FILE *cfp;
- short ttyflag, debugflag, linkflag, scriptflag,
- X mrbstart, /* ring buffer start pointer */
- X mrbcount, /* ring buffer counter */
- X BREAK = 0; /* a hook for a later 'trap' keyword */
- extern short eofflag;
- extern int redial(), s_set(), s_exit(), xmitbrk();
- extern void divert(), set_onoff(), xcdial();
- X
- int my_escape = 1; /* Control-A; resettable at run-time */
- X
- char mringbuf[LG_BUFF]; /* ring buffer for modem input */
- static S_abort();
- static void unsetall(), S_bombout(), S_call();
- static char *NO_ARG = "%s command must have an argument";
- X
- static bindfunc_t find_function(); /* bindfunc_t : see xc.h */
- X
- typedef struct bindstruct {
- X int bs_c; /* Character prefix */
- X int bs_function;/* Function code */
- X char *bs_string; /* String/script to emit */
- X struct bindstruct *bs_next; /* Pointer to next entry */
- X} binding_t;
- static binding_t *first_binding = NIL(binding_t);
- X
- typedef struct {
- X bindfunc_t bf_function;/* Function code */
- X char *bf_name; /* bind_function() name */
- X char *bf_string; /* String/script assigned */
- X} bindstr_t;
- X
- static bindstr_t function_list[] = {
- X {ENDCHAR, "endchar", "Exit terminal mode"},
- X {QUITCHR, "quitchr", "Quit program"},
- X {CAPTYES, "captyes", "Turn on terminal mode capture"},
- X {CAPTEND, "captend", "Turn off terminal mode capture"},
- X {DIVCHAR, "divchar", "Send file through modem"},
- X {DIALCHR, "dialchr", "Dial from phonelist"},
- X {HUPCHAR, "hupchar", "Hang up modem"},
- X {SCRPCHR, "scrpchr", "Prompt for script file"},
- X {HLPCHAR, "hlpchar", "Display terminal mode key bindings"},
- X {BRKCHAR, "brkchar", "Send modem BREAK signal"},
- X {EMITSTR, "emitstr", "Emit string"},
- X {DOSCRPT, "doscrpt", "Execute script file"}
- X};
- X
- X#define FUNCTION_COUNT (sizeof(function_list) / sizeof(function_list[0]))
- X
- static void
- newsigint(junk)
- int junk;
- X{
- X eofflag++;
- X show_abort();
- X S_bombout();
- X longjmp(here,1);
- X}
- X
- void
- do_script(file)
- char *file;
- X{
- X void (*oldvec)();
- X
- X capture = eofflag = FALSE;
- X ttyflag = scriptflag = TRUE;
- X
- X oldvec = signal(SIGINT, newsigint);
- X if (!setjmp(here))
- X S_call(file);
- X
- X unsetall();
- X if (capture)
- X capture = FALSE,
- X fclose(cfp);
- X
- X linkflag = scriptflag = FALSE;
- X
- X signal(SIGINT, oldvec);
- X}
- X
- static
- k_seen(bytes,fword)
- long bytes;
- char *fword;
- X{
- X int i, j, k;
- X char *cptr;
- X
- X cptr = fword;
- X
- X if (!fword || !*fword)
- X sprintf(Msg,NO_ARG,"SEEN"),
- X S2(Msg);
- X
- X j = mrbstart - 1;
- X if (bytes<=0 || bytes>LG_BUFF)
- X bytes = LG_BUFF;
- X k = mrbcount - bytes; /* check only most recent 'bytes' bytes */
- X i = 0;
- X while ((i++)<mrbcount){
- X ++j;
- X j = j % LG_BUFF;
- X if (i<k)
- X continue;
- X if (mringbuf[j] != *cptr){
- X cptr = fword;
- X continue;
- X }
- X if (*(++cptr)=='\0')
- X return SUCCESS;
- X }
- X return FAILURE;
- X}
- X
- k_waitfor(interval,fword)
- long interval;
- char *fword;
- X{
- X register c, i = -1 ;
- X register long limit, waitfor_msec = 0;
- X char *ptr;
- X struct tms tbuf;
- X
- X mrbstart = mrbcount = 0;
- X sprintf(line,"\"%s\"",fword);
- X lptr = line;
- X getword();
- X lc_word(word);
- X ptr = word;
- X
- X if (interval < -1){
- X waitfor_msec = -interval;
- X goto SPITOUT;
- X }
- X if (!fword || word[0] == '\0'){
- X sprintf(Msg,NO_ARG,"WAITFOR");
- X S2(Msg);
- X return FAILURE;
- X }
- X
- X waitfor_msec = 1000 * ((interval > 0) ? interval : 30);
- X eofflag = FALSE;
- X
- SPITOUT: limit = times(&tbuf) + (HZ * waitfor_msec)/1000;
- X while (limit >= times(&tbuf) && !eofflag){
- X if ((c = readbyte(1)) == -1)
- X continue;
- X
- X if (cismode && c==ENQ){
- X s_cis();
- X goto SPITOUT;
- X }
- X
- X ++i;
- X i = i % LG_BUFF;
- X mringbuf[i] = c;
- X mrbstart = mrbstart % LG_BUFF;
- X if (mrbcount<LG_BUFF)
- X ++mrbcount;
- X else
- X ++mrbstart,
- X mrbstart = mrbstart % LG_BUFF;
- X
- X if (ttyflag)
- X fputc(c,tfp);
- X
- X if (capture && c != '\r')
- X fputc(c,cfp);
- X
- X if (tolower(c) != *ptr){
- X ptr = word;
- X continue;
- X }
- X
- X if (*++ptr == '\0')
- X return SUCCESS;
- X }
- X return FAILURE;
- X}
- X
- static
- k_transmit(junk,fword)
- long junk;
- char *fword;
- X{
- X sprintf(line,"\"%s\"",fword);
- X lptr = line;
- X getword();
- X if (!fword || word[0] == '\0'){
- X sprintf(Msg,NO_ARG,"TRANSMIT");
- X S2(Msg);
- X return FAILURE;
- X }
- X send_string(word);
- X return SUCCESS;
- X}
- X
- static
- k_pause(pause_time,junk)
- long pause_time;
- char *junk;
- X{
- X pause_time = pause_time ? pause_time : 5;
- X sleep((unsigned)pause_time);
- X return SUCCESS;
- X}
- X
- static
- k_dial(junk,fword)
- long junk;
- char *fword;
- X{
- X sprintf(line,"%s",fword);
- X lptr = line;
- X getword();
- X if (!fword || word[0] == '\0'){
- X sprintf(Msg,NO_ARG,"DIAL");
- X S2(Msg);
- X return FAILURE;
- X }
- X xcdial(word);
- X return SUCCESS;
- X}
- X
- static
- k_capture(junk,fword)
- long junk;
- char *fword;
- X{
- X int val = capture;
- X
- X sprintf(word,"capture");
- X sprintf(line,"%s",fword);
- X lptr = line;
- X set_onoff(&capture);
- X
- X if (val == capture)
- X return SUCCESS;
- X
- X if (!capture)
- X fclose(cfp);
- X else {
- X if (!(cfp = fopen(captfile, "a"))){
- X sprintf(Msg,"Can't open capture file %s",captfile);
- X S2(Msg);
- X eofflag++;
- X return FAILURE;
- X }
- X setbuf(cfp,NIL(char));
- X }
- X return SUCCESS;
- X}
- X
- static
- k_debug(junk,fword)
- long junk;
- char *fword;
- X{
- X sprintf(word,"debug");
- X sprintf(line,"%s",fword);
- X lptr = line;
- X set_onoff(&debugflag);
- X return SUCCESS;
- X}
- X
- static
- k_tty(junk,fword)
- long junk;
- char *fword;
- X{
- X sprintf(word,"tty");
- X sprintf(line,"%s",fword);
- X lptr = line;
- X set_onoff(&ttyflag);
- X return SUCCESS;
- X}
- X
- static
- k_type(junk,fword)
- long junk;
- char *fword;
- X{
- X sprintf(line,"%s",fword);
- X lptr = line;
- X getword();
- X if (!fword || word[0] == '\0'){
- X sprintf(Msg,NO_ARG,"TYPE");
- X S2(Msg);
- X return FAILURE;
- X }
- X divert(TRUE);
- X return SUCCESS;
- X}
- X
- static
- k_linked()
- X{
- X return linkflag;
- X}
- X
- X/* unbind_key() removes the binding attached to the keycode specified by (c).*/
- static void
- unbind_key(c)
- int c;
- X{
- X binding_t *ptr = first_binding, *prev = NIL(binding_t);
- X if (!ptr)
- X return;
- X
- X while (ptr) {
- X if (ptr->bs_c == c) {
- X if (ptr->bs_string)
- X free(ptr->bs_string);
- X if (prev)
- X prev->bs_next = ptr->bs_next;
- X else
- X first_binding->bs_next = ptr->bs_next;
- X free(ptr);
- X return;
- X }
- X prev = ptr;
- X ptr = ptr->bs_next;
- X }
- X}
- X
- X/* bind_key() binds the key whose ASCII code is specified by (c) to the
- X function whose code is specified by (function). Emit strings
- X (for EMITSTR) and script names (for DOSCRPT) are specified by (string).
- X*/
- static void
- bind_key(c,function,string)
- int c;
- int function;
- char *string;
- X{
- X binding_t *ptr = (binding_t *) malloc(sizeof(binding_t)), *curr, *prev;
- X
- X if (!ptr) {
- X S2("BIND_KEY allocation error");
- X S_abort();
- X }
- X
- X unbind_key(c);
- X
- X ptr->bs_c = c;
- X ptr->bs_function = function;
- X ptr->bs_string = strdup(string);
- X
- X /* The following is an insertion sort to ensure that the bindings are
- X stored in ascending sequence by key code. This makes
- X show_bindings()'s display easier to read. -lg
- X */
- X
- X for (prev = NIL(binding_t), curr = first_binding;
- X curr != NIL(binding_t);
- X prev = curr, curr = curr->bs_next) {
- X if (ptr->bs_c < curr->bs_c) {
- X if (!prev) {
- X ptr->bs_next = first_binding;
- X first_binding = ptr;
- X break;
- X } else {
- X ptr->bs_next = curr;
- X prev->bs_next = ptr;
- X break;
- X }
- X }
- X }
- X
- X if (curr == NIL(binding_t)) {
- X if (!prev)
- X first_binding = ptr;
- X else
- X prev->bs_next = ptr;
- X ptr->bs_next = NIL(binding_t);
- X }
- X}
- X
- X/* bind_function() Binds the key whose code is represented by the value in
- X (n) to execute the XC builtin function whose name is pointed to be (cptr).
- X Any previous binding of the specified keycode is forgotten.
- X This routine is designed to be an XC script builtin.
- X*/
- static
- bind_function(n,cptr)
- long n;
- char *cptr;
- X{
- X int c;
- X bindfunc_t code;
- X
- X if (n < 1L || n > 127L) {
- X sprintf(Msg,"Invalid key code %d in BIND_FUNCTION command",n);
- X S2(Msg);
- X return FAILURE;
- X }
- X
- X c = tolower((int) n);
- X
- X sprintf(line, "\"%s\"", cptr);
- X lptr = line;
- X getword();
- X if (cptr && cptr[0] != '\0') {
- X lc_word(word);
- X code = find_function(word);
- X if (code == BADFUNC) {
- X sprintf(Msg,"Invalid function name '%s' in BIND_FUNCTION command",
- X word);
- X S2(Msg);
- X return FAILURE;
- X }
- X bind_key(c, code, word);
- X } else {
- X sprintf(Msg,NO_ARG,"BIND_FUNCTION");
- X S2(Msg);
- X return FAILURE;
- X }
- X
- X return SUCCESS;
- X}
- X
- X/* bind_string() binds the key whose code is represented by the
- X decimal value in (n) to emit the string pointed to by (cptr).
- X Any previous binding of the specified keycode is forgotten.
- X This routine is designed to be an XC script builtin.
- X*/
- static
- bind_string(n,cptr)
- long n;
- char *cptr;
- X{
- X int c;
- X
- X if (n < 1L || n > 127L) {
- X sprintf(Msg,"Invalid key code %d in BIND_STRING command",n);
- X S2(Msg);
- X return FAILURE;
- X }
- X
- X c = tolower((int) n);
- X
- X sprintf(line, "\"%s\"", cptr);
- X lptr = line;
- X getword();
- X if (cptr && cptr[0] != '\0')
- X bind_key(c, EMITSTR, word);
- X else {
- X sprintf(Msg,NO_ARG,"BIND_STRING");
- X S2(Msg);
- X return FAILURE;
- X }
- X
- X return SUCCESS;
- X}
- X
- X/* bind_script() binds the key whose code is represented by the value in
- X (n) to execute the XC script whose name is pointed to by (cptr).
- X Any previous binding of the specified keycode is forgotten.
- X This routine is designed to be an XC script builtin.
- X*/
- static
- bind_script(n,cptr)
- long n;
- char *cptr;
- X{
- X int c;
- X
- X if (n < 1L || n > 127L) {
- X sprintf(Msg,"Invalid key code %d in BIND_STRING command",n);
- X S2(Msg);
- X return FAILURE;
- X }
- X
- X c = tolower((int) n);
- X
- X sprintf(line, "\"%s\"", cptr);
- X lptr = line;
- X getword();
- X if (cptr && cptr[0] != '\0')
- X bind_key(c, DOSCRPT, word);
- X else {
- X sprintf(Msg,NO_ARG,"BIND_SCRIPT");
- X S2(Msg);
- X return FAILURE;
- X }
- X
- X return SUCCESS;
- X}
- X
- X/* Variables Section */
- X/* Most of the variable-handling logic is credit: Steve Manes 1987 */
- X#define VNAMELEN 16 /* maximum name length for variables */
- X#define VMAXSIZE 256 /* maximum length for CHAR variable */
- X#define VMAXVARS 30 /* maximum number of user variables */
- X#define VCHAR 'C' /* CHARACTER variable type */
- X#define VNUM 'N' /* NUMERIC variable type (always 'long') */
- X
- X/* Variable structure */
- typedef struct var {
- X char name[VNAMELEN+1]; /* variable name */
- X struct var *next; /* ptr to next structure in var_list */
- X char type; /* variable type */
- X union { /* pointer to CHAR or NUM/DATE */
- X char str[VMAXSIZE+1];
- X long num;
- X } u;
- X} VAR;
- X
- static VAR *Varlist = NIL(VAR); /* top of variable list */
- static VAR *Lastvar = NIL(VAR); /* bottom of variable list */
- X
- X/* Valid variable name characters */
- unchar
- OKname[]= {
- X /* control characters */
- X 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- X /* ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ */
- X 1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,
- X /* A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` */
- X 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,
- X /* a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ */
- X 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0
- X};
- X
- X/* Return pointer to VAR structure for User or System variable
- X 'name', otherwise return NIL(VAR).
- X Variable contents in vp->u.[str|num]
- X*/
- static VAR *
- findvar(name)
- char *name;
- X{
- X static VAR *vp; /* pointer to output structure */
- X
- X if (!name || !*name || !Varlist)
- X return NIL(VAR);
- X vp = Varlist;
- X while (vp){
- X if (!strncmp(name, vp->name, VNAMELEN) )
- X return vp;
- X vp = vp->next;
- X }
- X return NIL(VAR); /* not found */
- X}
- X
- X/* Delete User variable 'name' from VAR list.
- X If variable doesn't exist, no error is returned
- X*/
- static void
- unsetvar(name)
- char *name;
- X{
- X VAR *p;
- X VAR *lastp;
- X
- X if (!name || !*name)
- X return;
- X for (p=Varlist; p; lastp = p, p = p->next){
- X if (!strncmp(name, p->name, VNAMELEN) ){ /* name match? */
- X if (Varlist == Lastvar) /* only 1 variable */
- X Varlist = Lastvar = NIL(VAR); /* in list */
- X else if (p == Varlist) /* first variable */
- X Varlist = p->next;
- X else {
- X lastp->next = p->next; /* dump variable in middle
- X of list */
- X if (p == Lastvar) /* or last object */
- X Lastvar = lastp; /* in list */
- X }
- X free(p); /* reclaim memory */
- X break;
- X }
- X }
- X}
- X
- X/* Set the value of User variable 'name' to 'val' with 'type'.
- X If variable exists, change its contents. Otherwise, create it.
- X Returns: FAILURE or SUCCESS
- X*/
- static
- setvar(name,type,str,val)
- char *name;
- char type;
- char *str;
- long *val;
- X{
- X VAR *vp;
- X short i;
- X
- X if (!name || !*name)
- X return FAILURE;
- X if (!(vp = findvar(name))){ /* create new variable */
- X for (i=0; i < VNAMELEN && name[i]; i++){
- X if( !OKname[(name[i] & 0x7F)] || isdigit(name[0]) ){
- X sprintf(Msg,"Illegal variable name '%s'",name);
- X S_abort();
- X }
- X }
- X if (!(vp = (VAR *)malloc(sizeof(VAR)))){
- X sprintf(Msg,"%s: allocation error",name);
- X S2(Msg);
- X return FAILURE;
- X }
- X lc_word(name);
- X strncpy(vp->name, name, VNAMELEN); /* set vari name */
- X vp->next = NIL(VAR); /* flag 'no next' */
- X if (!Varlist)
- X Varlist = vp; /* first variable */
- X else
- X Lastvar->next = vp; /* add this to the list */
- X Lastvar = vp; /* set 'last' pointer */
- X }
- X
- X if (type == VCHAR)
- X strncpy(vp->u.str, str, VMAXSIZE);
- X else
- X vp->u.num = *val;
- X vp->type = type;
- X return SUCCESS;
- X}
- X
- X/* Unset all user variables, deallocating memory.
- X No error returned
- X*/
- static void
- unsetall()
- X{
- X VAR *p;
- X VAR *nextp;
- X
- X if (!Varlist)
- X return;
- X p = Varlist;
- X while (p->next){
- X nextp = p->next;
- X free(p);
- X p = nextp;
- X }
- X Varlist = Lastvar = NIL(VAR);
- X}
- X/* end variables section */
- X
- X/* Action Primitives */
- static struct {
- X char *name;
- X int (*funcptr)();
- X} s_acttab[] = {
- X {"beep", beep},
- X {"bind_function", bind_function},
- X {"bind_script", bind_script},
- X {"bind_string", bind_string},
- X {"capture", k_capture},
- X {"debug", k_debug},
- X {"dial", k_dial},
- X {"hangup", hangup},
- X {"linked", k_linked},
- X {"pause", k_pause},
- X {"quit", s_exit},
- X {"redial", redial},
- X {"seen", k_seen},
- X {"xmitbrk", xmitbrk},
- X {"transmit", k_transmit},
- X {"tty", k_tty},
- X {"type", k_type},
- X {"waitfor", k_waitfor},
- X {NIL(char), 0}
- X};
- X/* end of primitives */
- X
- X/* token types */
- typedef enum {
- X NULLTOK, /* terminating '\0' in script buffer */
- X ACTION, /* an action (primitive or script cmd) */
- X AFFIRM, /* script 'affirm' */
- X BACKQUOT, /* script command substitution */
- X SBREAK, /* script 'break' */
- X CALL, /* script 'call' */
- X COMMENT, /* comment */
- X SCONTNUE, /* script 'continue' */
- X DECR, /* script 'decr' */
- X DO, /* script 'do' */
- X DONE, /* script 'done' */
- X ECHOS, /* script 'echo' */
- X EFLAG, /* '-n' switch for script 'echo' cmd */
- X ELSE, /* script 'else' */
- X ENDIF, /* script 'endif' */
- X ENDTRAP, /* script 'endtrap' */
- X EQ, /* operator "equals" */
- X EXIT, /* script 'exit' */
- X SFILE, /* script 'file' */
- X SFALSE, /* script 'false' */
- X IF, /* script 'if' */
- X INCR, /* script 'incr' */
- X LESSTHAN, /* operator "less than" */
- X LITERAL, /* a literal string (e.g. "abcde") */
- X MORETHAN, /* operator "greater than" */
- X NEGATE, /* negation operator for comparisons */
- X NEQ, /* operator "not equal to" */
- X NUMBER, /* a numeric constant (e.g. 12345) */
- X PIPE, /* script 'pipe' */
- X READ, /* script 'read' */
- X SHELL, /* script 'shell' */
- X SET, /* script 'assign' */
- X STRAP, /* script 'trap' */
- X TERMINAT, /* statement terminators (';' and '\n') */
- X THEN, /* script 'then' */
- X STRUE, /* script 'true' */
- X UNSET, /* script 'unset' */
- X UNTRAP, /* script 'untrap' */
- X XCSET, /* xc 'set' command */
- X VARNAME, /* a variable name */
- X WHILE, /* script 'while' */
- X TTERROR, /* unrecognizable token */
- X TIMEOUT /* script 'timeout' */
- X} TOK_TYPE;
- X
- X/* token table */
- static struct {
- X char *name;
- X TOK_TYPE token;
- X} s_toktab[] = {
- X {"NULLTOK", NULLTOK},
- X {"ACTION", ACTION},
- X {"affirm", AFFIRM},
- X {"BACKQUOT", BACKQUOT},
- X {"break", SBREAK},
- X {"call", CALL},
- X {"COMMENT", COMMENT},
- X {"continue", SCONTNUE},
- X {"decr", DECR},
- X {"do", DO},
- X {"done", DONE},
- X {"echo", ECHOS},
- X {"-n", EFLAG},
- X {"else", ELSE},
- X {"endif", ENDIF},
- X {"fi", ENDIF},
- X {"ENDTRAP", ENDTRAP},
- X {"eq", EQ},
- X {"exit", EXIT},
- X {"false", SFALSE},
- X {"file", SFILE},
- X {"if", IF},
- X {"incr", INCR},
- X {"lessthan", LESSTHAN},
- X {"LITERAL", LITERAL},
- X {"morethan", MORETHAN},
- X {"!", NEGATE},
- X {"neq", NEQ},
- X {"NUMBER", NUMBER},
- X {"pipe", PIPE},
- X {"read", READ},
- X {"assign", SET},
- X {"shell", SHELL},
- X {"TRAP", STRAP}, /* 'trap' keyword left for later dev't */
- X {"TERMINAT", TERMINAT},
- X {"then", THEN},
- X {"timeout", TIMEOUT},
- X {"true", STRUE},
- X {"unassign", UNSET},
- X {"UNTRAP", UNTRAP},
- X {"set", XCSET},
- X {"VARNAME", VARNAME},
- X {"while", WHILE},
- X {"\0", TTERROR}
- X};
- X/* end token types */
- X
- X/* tok_value is set by lexan() in the following instances:
- X TOK_TYPE NUMBER: (long) value of number
- X TOK_TYPE LITERAL: pointer to beginning of quoted string
- X TOK_TYPE ACTION: function pointer to appropriate vector
- X TOK_TYPE VARNAME: pointer to VAR struct
- X TOK_TYPE TTERROR: pointer to strange construction in script
- X All other values of TOK_TYPE don't require further information.
- X*/
- static union {
- X long numval; /* numbers */
- X char *strptr; /* for literal strings (points to initial '"') */
- X int (*funcptr)(); /* vector for primitives */
- X VAR *varptr; /* for variables */
- X} tok_value;
- X
- X
- X/* lexan() is the lexical analyzer, which translates words
- X into token types and sets tok_value appropriately. It's
- X called repeatedly by the language parser, S_parse().
- X*/
- static TOK_TYPE
- lexan(pcptr)
- char **pcptr; /* address of script program counter */
- X{
- X long nvalue, negpos = 1;
- X VAR *varptr;
- X FILE *bqpipe;
- X int i, c; /* really a char, but 'int' to spot EOF */
- X static char *cptr, *lasttok,
- X latoken[VNAMELEN+1], temp[VMAXSIZE+1], bqcmd[VMAXSIZE+1];
- X extern FILE *popen();
- X
- X /* if in debug mode, echo script line to tfp */
- X if (debugflag && *pcptr>lasttok){
- X cptr = *pcptr - 1;
- X while (*cptr==' ' || *cptr=='\t') --cptr;
- X if (*cptr=='\n'){
- X fputs("+ ",tfp);
- X ++cptr;
- X while (*cptr!='\n' && *cptr)
- X fputc(*(cptr++),tfp);
- X fputc('\r',tfp),
- X fputc('\n',tfp);
- X }
- X }
- X
- X /* skip to beginning of next token */
- X while (**pcptr==' ' || **pcptr=='\t') ++(*pcptr);
- X tok_value.strptr = cptr = lasttok = *pcptr; /* save place */
- X
- X /* negation operator for comparisons */
- X if (*cptr=='!' && (*(cptr+1)==' ' || *(cptr+1)=='\t')){
- X ++cptr;
- X *pcptr = cptr;
- X return NEGATE;
- X }
- X /* comment in script */
- X if (*cptr=='#'){
- X while (*cptr && *cptr!='\n') ++cptr;
- X *pcptr = cptr;
- X return TERMINAT;
- X }
- X /* statement terminator */
- X if (*cptr==';' || *cptr=='\n'){
- X ++cptr;
- X *pcptr = cptr;
- X return TERMINAT;
- X }
- X /* end of script */
- X if (*cptr=='\0')
- X return NULLTOK;
- X /* quoted literal string */
- X if (*cptr=='"'){
- X ++cptr;
- X while (*cptr && *cptr!='\n' && !(*cptr=='"' && *(cptr-1)!='\\'))
- X ++cptr;
- X if (*cptr=='"'){
- X ++cptr;
- X *pcptr = cptr;
- X return LITERAL;
- X }
- X sprintf(Msg,"Unmatched quote");
- X S_abort();
- X }
- X /* environment variable (treat as a literal) */
- X if (*cptr=='$'){
- X ++cptr;
- X for (i=0; i<VMAXSIZE; ++i){
- X if (!*cptr || *cptr==' ' || *cptr=='\t' || *cptr=='\n'
- X || *cptr=='\r' || *cptr==';')
- X break;
- X temp[i] = *(cptr++);
- X }
- X temp[i] = '\0';
- X tok_value.strptr = getenv(temp);
- X if (!tok_value.strptr)
- X tok_value.strptr = temp,
- X sprintf(Msg,"%s: no such environment variable",temp),
- X S2(Msg),
- X tok_value.strptr = "";
- X *pcptr = cptr;
- X return LITERAL;
- X }
- X /* back-quoted shell command (treat like env var) */
- X if (*cptr=='`'){
- X ++cptr;
- X i = 0;
- X while (*cptr && *cptr!='\n' && *cptr!='`' && (++i)<VMAXSIZE)
- X ++cptr;
- X if (*cptr=='`'){
- X for (i=0; i<VMAXSIZE; ++i){ /* tok_value ptr points */
- X bqcmd[i] = tok_value.strptr[i+1]; /* to leading '`' */
- X if (bqcmd[i]=='`'){
- X bqcmd[i] = '\0';
- X break;
- X }
- X }
- X bqcmd[i] = '\0';
- X signal(SIGCLD,SIG_DFL);
- X if (!(bqpipe=popen(bqcmd,"r"))){
- X sprintf(Msg,"%s: cannot create pipe",bqcmd);
- X S_abort();
- X }
- X else {
- X temp[0] = '\0';
- X i = 0;
- X while (i<=VMAXSIZE && (c=fgetc(bqpipe))!=EOF && c!='\n')
- X temp[i++] = c;
- X fflush(bqpipe);
- X pclose(bqpipe);
- X temp[i] = '\0';
- X tok_value.strptr = temp;
- X *pcptr = cptr + 1;
- X return LITERAL;
- X }
- X }
- X else {
- X sprintf(Msg,"Unmatched back-quote:");
- X S_abort();
- X }
- X }
- X /* dialout port name */
- X if (!strncmp(cptr,"portname",8)){
- X tok_value.strptr =mport(NIL(char));
- X *pcptr += 8;
- X return LITERAL;
- X }
- X /* leading hyphen, maybe a negative number? */
- X if (*cptr=='-')
- X negpos = (-1),
- X ++cptr;
- X /* string beginning with a digit */
- X if (isdigit(*cptr)){
- X nvalue = (*cptr - '0') * negpos;
- X while (*(++cptr)){
- X if (isdigit(*cptr)){
- X nvalue *= 10;
- X nvalue += (*cptr - '0');
- X if (nvalue>0) nvalue *= negpos;
- X continue;
- X }
- X else if (strchr(" \t\n;",*cptr)){
- X tok_value.numval = nvalue;
- X *pcptr = cptr;
- X return NUMBER;
- X }
- X sprintf(Msg,"Variable name cannot begin with a digit: ");
- X S_abort();
- X }
- X tok_value.numval = nvalue;
- X *pcptr = cptr;
- X return NUMBER;
- X }
- X /* check for '-n' switch for echo (type EFLAG) */
- X if (negpos<0){
- X if (*cptr=='N' || *cptr=='n'){
- X while (*cptr && !strchr(" \t\n;",*cptr)) ++cptr;
- X *pcptr = cptr;
- X return EFLAG;
- X }
- X sprintf(Msg,"Bad option to ECHO command");
- X S_abort();
- X }
- X /* impermissible initial character */
- X if (!isalpha(*cptr)){
- X sprintf(Msg,"bad initial character: %c", *cptr);
- X S_abort();
- X }
- X
- X /* remember that tok_value.strptr points to start of token */
- X for (i=1; i<(VNAMELEN+1); ++i){ /* jump to next field separator */
- X ++cptr;
- X if (*cptr=='\0' || strchr(" \t\n;",*cptr))
- X break;
- X }
- X if (i>VNAMELEN){ /* word too long */
- X sprintf(Msg,"Variable name too long");
- X S_abort();
- X }
- X strncpy(latoken,tok_value.strptr,i); /* copy word to array 'latoken' */
- X latoken[i] = '\0';
- X lc_word(latoken); /* cvt to lowercase */
- X /* script keywords */
- X /* scan table for keyword match */
- X for (i=0; *(s_toktab[i].name) && strcmp(latoken,s_toktab[i].name); ++i)
- X ;
- X if (*s_toktab[i].name) { /* lc keyword recognized */
- X if (s_toktab[i].token==STRUE)
- X tok_value.numval = TRUE;
- X if (s_toktab[i].token==SFALSE)
- X tok_value.numval = FALSE;
- X *pcptr = cptr;
- X return s_toktab[i].token;
- X }
- X /* system primitive (ACTION) */
- X /* scan table for keyword match */
- X for (i=0;s_acttab[i].name && strcmp(latoken,s_acttab[i].name);++i)
- X ;
- X if (s_acttab[i].name){ /* primitive recognized */
- X tok_value.funcptr = s_acttab[i].funcptr;
- X *pcptr = cptr;
- X return ACTION;
- X }
- X /* user variable name */
- X if ((varptr=findvar(latoken))){ /* existing user variable */
- X tok_value.varptr = varptr;
- X *pcptr = cptr;
- X return VARNAME;
- X }
- X /* could this be the name of a new variable? */
- X tok_value.strptr = latoken; /* just in case this is 'on', 'off', etc. */
- X if (!setvar(latoken,VCHAR,"",0)) /* can't create it */
- X return TTERROR;
- X if (!(varptr=findvar(latoken))) /* can't retrieve it */
- X return TTERROR;
- X else { /* got it */
- X tok_value.varptr = varptr;
- X *pcptr = cptr;
- X return VARNAME;
- X }
- X}
- X
- X/* utility routines called by S_parse() */
- X
- X/* S_affirm is a placeholder. It's the function to get a yes or no
- X response from the user.
- X*/
- static
- S_affirm()
- X{
- X char c, junk;
- X
- X c = getchar();
- X fputc(c,tfp);
- X while ((junk=getchar()) !='\n' && junk !='\r')
- X fputc(junk,tfp);
- X
- X fputc('\r',tfp),
- X fputc('\n',tfp);
- X return(c=='y' || c=='Y');
- X}
- X
- X/* S_addsub increments or decrements a numeric variable.
- X It assumes that since the INCR or DECR directives call
- X lexan() for the variable just before coming here, the
- X tok_value structure contains a pointer to the variable
- X whose value is to be changed.
- X*/
- static
- S_addsub(direction)
- int direction;
- X{
- X long oldval = tok_value.varptr->u.num;
- X oldval += direction;
- X
- X if (!setvar(tok_value.varptr->name,VNUM,"",&oldval)){
- X sprintf(Msg,"Error setting variable '%s'", tok_value.varptr->name);
- X S_abort();
- X }
- X return(tok_value.varptr->u.num ? SUCCESS : FAILURE);
- X}
- X
- X/* S_qstrip returns a pointer to a (static) string with leading and
- X trailing double-quote marks removed. If the string happens to
- X lack a leading or trailing double-quote mark, then the string
- X will be returned with its beginning unchanged, and its length
- X equal to VMAXSIZE or the length of the string, whichever is
- X shorter. Double-quote marks escaped with a backslash are included
- X in the returned string and the backslash is excised.
- X*/
- static char *
- S_qstrip(strptr)
- char *strptr;
- X{
- X int i;
- X static char strbuf[VMAXSIZE+2];
- X
- X if (*strptr=='"')
- X ++strptr;
- X for (i=0; i<VMAXSIZE+1; ++i){
- X if (*strptr=='\\' && *(strptr+1)=='"' && *(strptr-1)!='\\')
- X ++strptr;
- X if ((*strptr=='"' && *(strptr-1)!='\\') || !*strptr || *strptr=='\n')
- X break;
- X strbuf[i] = *strptr;
- X ++strptr;
- X }
- X strbuf[i] = '\0';
- X return strbuf;
- X}
- X
- X/* S_read does the parsing grunts for the script 'read' directive. On
- X entry, the 'read' token has been parsed, but that's all.
- X*/
- static
- S_read(pcptr)
- char **pcptr;
- X{
- X int i;
- X VAR *varptr1;
- X static char strbuf[VMAXSIZE+2];
- X
- X if (lexan(pcptr)!=VARNAME)
- X S_abort();
- X varptr1 = tok_value.varptr;
- X strbuf[0] = '\0';
- X for (i=0; i<VMAXSIZE; ++i){
- X strbuf[i] = getchar();
- X if (strbuf[i]==BS){
- X if (i>0)
- X fputs("\b \b",tfp),
- X i -= 2;
- X else
- X i = -1;
- X continue;
- X }
- X fputc(strbuf[i],tfp);
- X if (strbuf[i]=='\n' || strbuf[i]=='\r'){
- X strbuf[i] = '\0';
- X break;
- X }
- X }
- X strbuf[VMAXSIZE] = '\0';
- X fputc('\r',tfp),
- X fputc('\n',tfp);
- X return(setvar(varptr1->name,VCHAR,strbuf,0));
- X}
- X
- X/* S_perform invokes a system primitive and returns SUCCESS if the
- X primitive succeeds, FAILURE if it fails. On entry, only the ACTION
- X token has been parsed.
- X*/
- static
- S_perform(pcptr)
- char **pcptr;
- X{
- X int (*fptr)();
- X char *cptr;
- X VAR *varptr1;
- X long n = -1;
- X
- X fptr = tok_value.funcptr;
- X cptr = NIL(char);
- X while (TRUE){
- X switch (lexan(pcptr)){
- X case TERMINAT:
- X break;
- X case NUMBER:
- X if (n != -1)
- X S_abort();
- X n = tok_value.numval;
- X continue;
- X case LITERAL:
- X case TTERROR:
- X cptr = S_qstrip(tok_value.strptr);
- X continue;
- X case VARNAME:
- X varptr1 = tok_value.varptr;
- X if (varptr1->type==VCHAR){
- X cptr = varptr1->u.str;
- X break;
- X }
- X if (n != -1)
- X S_abort();
- X n = varptr1->u.num;
- X continue;
- X default:
- X S_abort();
- X }
- X break;
- X }
- X return (*fptr)(n,cptr);
- X}
- X
- X/* S_set does the parsing grunts for the script 'assign' directive. It's
- X a separate function mostly to keep from cluttering up S_parse()
- X too much. On entry, only the 'set' token has been received from
- X the directive containing it.
- X*/
- static
- S_set(pcptr)
- char **pcptr; /* S_parse()'s program counter (p_pc) */
- X{
- X TOK_TYPE nexttype;
- X VAR *varptr1, *varptr2;
- X char *setstr;
- X
- X if ((nexttype=lexan(pcptr))!=VARNAME)
- X S_abort();
- X varptr1 = tok_value.varptr;
- X if ((nexttype=lexan(pcptr))!=EQ)
- X S_abort();
- X switch (nexttype = lexan(pcptr)){
- X case LITERAL:
- X setstr = S_qstrip(tok_value.strptr);
- X return(setvar(varptr1->name,VCHAR,setstr,0));
- X case ACTION:
- X case AFFIRM:
- X if (nexttype==ACTION)
- X tok_value.numval = (long) S_perform(pcptr);
- X else
- X tok_value.numval = (long) S_affirm();
- X case NUMBER:
- X case STRUE:
- X case SFALSE:
- X return(setvar(varptr1->name,VNUM,"",&tok_value.numval));
- X case VARNAME:
- X varptr2 = tok_value.varptr;
- X switch (varptr2->type){
- X case VCHAR:
- X return(setvar(varptr1->name,VCHAR,varptr2->u.str,0));
- X default:
- X return(setvar(varptr1->name,VNUM,"",&(varptr2->u.num)));
- X }
- X default:
- X S_abort();
- X }
- X}
- X
- X/* S_varcmp() compares a variable's value with a string or numeric
- X literal, or with the value of a second variable. Once again,
- X this function does parsing grunts for S_parse().
- X*/
- static
- S_varcmp(varptr1, pcptr)
- VAR *varptr1;
- char **pcptr;
- X{
- X TOK_TYPE compmode;
- X long testnum;
- X static char strbuf[VMAXSIZE+1];
- X char *cmpstr;
- X int status, numvar;
- X VAR *varptr2;
- X
- X numvar = (varptr1->type!=VCHAR);
- X compmode = lexan(pcptr);
- X switch (compmode){
- X case EQ:
- X case NEQ:
- X case MORETHAN:
- X case LESSTHAN:
- X break;
- X case TERMINAT:
- X if (numvar)
- X return(varptr1->u.num ? TRUE : FALSE);
- X else
- X return(*varptr1->u.str ? TRUE : FALSE);
- X default:
- X S_abort();
- X }
- X switch (lexan(pcptr)){
- X case LITERAL:
- X if (numvar){
- X sprintf(Msg,"Error: %s is a numeric variable",varptr1->name);
- X S_abort();
- X }
- X ++tok_value.strptr;
- X strncpy(strbuf,tok_value.strptr,VMAXSIZE);
- X *(strchr(strbuf,'"')) = '\0';
- X cmpstr = strbuf;
- X break;
- X case NUMBER:
- X case STRUE:
- X case SFALSE:
- X if (!numvar){
- X sprintf(Msg,"Error: %s is a string variable",varptr1->name);
- X S_abort();
- X }
- X testnum = tok_value.numval;
- X break;
- X case VARNAME:
- X varptr2 = tok_value.varptr;
- X if (numvar && varptr2->type==VCHAR){
- X sprintf(Msg,"Error: %s and %s are of different types",
- X varptr1->name, varptr2->name);
- X S_abort();
- X }
- X if (numvar)
- X testnum = varptr2->u.num;
- X else
- X cmpstr = varptr2->u.str;
- X break;
- X default:
- X S_abort();
- X }
- X if (numvar){
- X status = (varptr1->u.num==testnum);
- X if (compmode==EQ)
- X return status;
- X if (compmode==NEQ)
- X return !status;
- X status = (varptr1->u.num > testnum);
- X if (compmode==MORETHAN)
- X return status;
- X else
- X return !status;
- X }
- X status = strcmp(varptr1->u.str,cmpstr);
- X if (compmode==EQ)
- X return(status==0);
- X if (compmode==NEQ)
- X return status;
- X if (compmode==MORETHAN)
- X return(status>0);
- X else
- X return(status<0);
- X}
- X
- static char *
- S_construct(pcptr)
- char **pcptr;
- X{
- X char *cptr;
- X static char newstring[VMAXSIZE+10];
- X TOK_TYPE nexttype;
- X
- X newstring[0] = '\0';
- X cptr = newstring;
- X while ((nexttype=lexan(pcptr))!=NULLTOK){
- X if (strlen(newstring)>VMAXSIZE){
- X newstring[VMAXSIZE] = '\0';
- X break;
- X }
- X switch (nexttype){
- X case TERMINAT:
- X break;
- X case NUMBER:
- X sprintf(cptr,"%ld",tok_value.numval);
- X cptr += strlen(cptr);
- X continue;
- X case LITERAL:
- X sprintf(cptr,"%s",S_qstrip(tok_value.strptr));
- X cptr += strlen(cptr);
- X continue;
- X case VARNAME:
- X if (tok_value.varptr->type != VCHAR)
- X sprintf(cptr,"%ld",tok_value.varptr->u.num);
- X else
- X sprintf(cptr,"%s",tok_value.varptr->u.str);
- X cptr += strlen(cptr);
- X continue;
- X default:
- X S_abort();
- X }
- X break;
- X }
- X return newstring;
- X}
- X
- X/* stack protection and break/continue stuff */
- X
- static int nest_while, /* number of nested loops */
- X nest_parse, /* number of nested calls to S_parse() */
- X nest_cmd; /* number of nested commands */
- X
- static long deadline; /* deadline for 'timeout' keyword */
- X
- jmp_buf env; /* cell for environment, setjmp */
- X
- X#define CMDNEST 8 /* max number of nested scripts */
- X#define PARSNEST 50 /* max number of nested calls to parser */
- X
- static char *areas[CMDNEST];
- X
- X#define DNP --nest_parse
- X#define BCCHK(x) if(x==SBREAK||x==SCONTNUE){DNP;return(nest_while?x:S_abort());}
- X
- X/* cleanup any debris left after a non-trapped keyboard interrupt */
- static void
- S_bombout()
- X{
- X int i;
- X
- X for (i=0; i<nest_cmd; ++i){
- X if (areas[i])
- X free(areas[i]),
- X areas[i] = NIL(char);
- X }
- X nest_cmd = nest_while = nest_parse = 0;
- X deadline = 0;
- X}
- X
- X/* S_parse() */
- static char *intercom, /* last previous value of S_parse program ctr */
- X *tvector; /* trap vector */
- static FILE *savetfp; /* to stash tfp when tfp redirected */
- static
- S_parse(p_pc, t_invoke)
- char *p_pc;
- TOK_TYPE t_invoke;
- X{
- X long n; /* "leading" number for primitives */
- X int i,
- X status, /* status of last performed operation */
- X retval, /* value to be returned by this function */
- X testing, /* flag set if within 'if' or 'while' clause */
- X direction, /* if 1, increment, if -1, decrement */
- X w_status, /* used to correct nest_parse in 'while' */
- X counter, /* for WHILE and IF, to track keywords */
- X negating; /* ! operator in effect for comparisons */
- X char *cptr, *S_WHILE, *S_DO, *S_DONE;
- X TOK_TYPE nexttype;
- X VAR *varptr1;
- X
- X ++nest_parse;
- X testing = (t_invoke==THEN || t_invoke==DO);
- X retval = status = negating = FALSE;
- X n = -1;
- X counter = 0;
- X while (TRUE){
- X /* we come through here only at the beginning of expressions */
- X if (deadline){
- X if (time(0)>deadline){ /* deadline; exit current script */
- X deadline = 0;
- X longjmp(env,TIMEOUT);
- X }
- X }
- X if (BREAK && tvector && t_invoke!=ENDTRAP) /* interrupt trap */
- X BREAK = FALSE,
- X cptr = tvector,
- X status = S_parse(cptr,ENDTRAP),
- X DNP;
- X
- X BREAK = FALSE;
- X retval = testing ? (retval || status) : status;
- X direction = 1;
- X intercom = p_pc;
- X nexttype = lexan(&p_pc);
- X /* check for list terminator */
- X if (nexttype==t_invoke || (t_invoke==ENDIF && nexttype==ELSE)){
- X if (debugflag && testing)
- X fprintf(tfp,"\r\n\t\t\t\t\t\t\tCondition: %s\r\n",
- X retval ? "TRUE" : "FALSE");
- X return retval;
- X }
- X switch (nexttype){
- X case NULLTOK: /* inconsistent list terminators */
- X case DO: /**/
- X case DONE: /**/
- X case THEN: /**/
- X case ELSE: /**/
- X case ENDIF: /**/
- X case TTERROR: /**/
- X case EFLAG: /* not at beginning of expressions */
- X case EQ: /**/
- X case NEQ: /**/
- X case MORETHAN: /**/
- X case LESSTHAN: /**/
- X S_abort();
- X case LITERAL:
- X if (!testing)
- X S_abort();
- X status = !!strlen(tok_value.strptr);
- X if (negating)
- X status = !status;
- X continue;
- X case NEGATE:
- X if (!testing || negating)
- X S_abort();
- X negating = 1;
- X continue;
- X case NUMBER:
- X if (n!=(-1))
- X S_abort();
- X n = tok_value.numval;
- X continue;
- X case TERMINAT:
- X negating = 0;
- X continue;
- X case ACTION:
- X status = S_perform(&p_pc);
- X if (negating)
- X status = !status;
- X break;
- X case AFFIRM:
- X status = S_affirm();
- X if (negating)
- X status = !status;
- X break;
- X case SBREAK:
- X case SCONTNUE:
- X if (testing || t_invoke==NULLTOK)
- X S_abort();
- X return(nexttype==SBREAK ? SBREAK : SCONTNUE);
- X case CALL:
- X lexan(&p_pc);
- X i = nest_while;
- X nest_while = 0;
- X S_call(S_qstrip(tok_value.strptr));
- X DNP;
- X nest_while = i;
- X break;
- X case COMMENT:
- X continue;
- X case DECR:
- X direction = (-1); /* and fall through to... */
- X case INCR:
- X if ((nexttype=lexan(&p_pc))!=VARNAME)
- X S_abort();
- X if ((tok_value.varptr->type) != VNUM){
- X sprintf(Msg,"Error: %s is not a numeric variable",
- X tok_value.varptr->name);
- X S_abort();
- X }
- X status = S_addsub(direction);
- X if (negating) status =
- X !status;
- X break;
- X case ECHOS:
- X status = 1;
- X if ((nexttype = lexan(&p_pc))==EFLAG)
- X status = 0;
- X else
- X p_pc = intercom,
- X lexan(&p_pc);
- X cptr = S_construct(&p_pc);
- X fprintf(tfp,"%s",cptr);
- X if (status)
- X if (tfp != cfp)
- X fputc('\r',tfp),
- X fputc('\n',tfp);
- X else
- X status = 1;
- X break;
- X case EXIT:
- X longjmp(env,EXIT);
- X case READ:
- X status = S_read(&p_pc);
- X break;
- X case SET:
- X status = S_set(&p_pc);
- X if (negating)
- X status = !status;
- X break;
- X case SHELL:
- X sprintf(word,"!");
- X sprintf(line,"%s",S_construct(&p_pc));
- X if (tfp==cfp)
- X sprintf(&line[strlen(line)]," >>%s",captfile);
- X lptr = line;
- X status = !s_shell();
- X if (negating)
- X status = !status;
- X break;
- X case PIPE:
- X sprintf(word,"$");
- X sprintf(line,"%s",S_construct(&p_pc));
- X lptr = line;
- X status = !s_shell();
- X if (negating)
- X status = !status;
- X break;
- X case SFILE:
- X savetfp = tfp;
- X if (!capture){
- X S2("Capture option not on");
- X while ((nexttype=lexan(&p_pc))!=TERMINAT &&
- X nexttype != NULLTOK);
- X break;
- X }
- X tfp = cfp;
- X status = S_parse(p_pc,TERMINAT);
- X if (negating)
- X status = !status;
- X DNP;
- X while ((nexttype=lexan(&p_pc)) !=TERMINAT &&
- X nexttype != NULLTOK)
- X ;
- X tfp = savetfp;
- X fseek(cfp,0L,2);
- X break;
- X case STRUE:
- X case SFALSE:
- X status = (nexttype==STRUE);
- X intercom = p_pc;
- X if ((nexttype=(lexan(&p_pc)))!=TERMINAT && nexttype!=NULLTOK)
- X S_abort();
- X if (negating)
- X status = !status;
- X break;
- X case STRAP:
- X tvector = p_pc;
- X cptr = tvector;
- X while ((nexttype=lexan(&p_pc))!=ENDTRAP)
- X if (nexttype==NULLTOK)
- X S_abort();
- X break;
- X case TIMEOUT:
- X if ((nexttype=lexan(&p_pc))!=NUMBER)
- X S_abort();
- X if (tok_value.numval>=0){
- X deadline = 0;
- X if (tok_value.numval)
- X deadline = time(0) + (tok_value.numval*60);
- X }
- X while ((nexttype=lexan(&p_pc))!=TERMINAT && nexttype!=NULLTOK)
- X ;
- X break;
- X case UNSET:
- X if ((nexttype=lexan(&p_pc))!=VARNAME)
- X S_abort();
- X status = 1;
- X unsetvar(tok_value.varptr->name);
- X break;
- X case UNTRAP:
- X tvector = NIL(char);
- X if ((nexttype=lexan(&p_pc))!=TERMINAT)
- X S_abort();
- X break;
- X case XCSET:
- X i = 0;
- X line[0] = '\0';
- X while (*p_pc!='\n')
- X line[i++] = *(p_pc++);
- X line[i] = '\0';
- X lptr = line;
- X s_set();
- X break;
- X case VARNAME:
- X varptr1 = tok_value.varptr;
- X if (!testing){
- X if (varptr1->type==VCHAR || n!=(-1))
- X S_abort();
- X n = varptr1->u.num;
- X continue;
- X }
- X status = S_varcmp(varptr1,&p_pc);
- X if (negating)
- X status = !status;
- X break;
- X case IF:
- X if (nest_parse > PARSNEST){
- X sprintf(Msg,"Nesting level too deep");
- X S_abort();
- X }
- X status = S_parse(p_pc,THEN);
- X DNP;
- X if (status==TRUE){
- X lexan(&intercom);
- X p_pc = intercom;
- X status = S_parse(p_pc,ENDIF);
- X BCCHK(status)
- X DNP;
- X cptr = intercom;
- X nexttype = lexan(&cptr);
- X p_pc = cptr;
- X if (nexttype==ELSE){
- X counter = 0;
- X while (TRUE){
- X switch ((nexttype=lexan(&cptr))){
- X case IF:
- X ++counter;
- X continue;
- X case ENDIF:
- X if (counter){
- X --counter;
- X continue;
- X }
- X p_pc = cptr;
- X break;
- X case NULLTOK:
- X S_abort();
- X default:
- X continue;
- X }
- X break;
- X } /* now p_pc points to just after matching 'endif' */
- X }
- X }
- X else { /* intercom is now the THEN token */
- X counter = 0;
- X cptr = intercom;
- X while (TRUE){
- X switch ((nexttype=lexan(&cptr))){
- X case IF:
- X ++counter;
- X continue;
- X case ELSE:
- X case ENDIF:
- X if (counter){
- X --counter;
- X continue;
- X }
- X p_pc = cptr;
- X break;
- X case NULLTOK:
- X S_abort();
- X default:
- X continue;
- X }
- X break;
- X }
- X if (nexttype==ELSE){
- X status = S_parse(p_pc,ENDIF);
- X BCCHK(status)
- X DNP;
- X p_pc = intercom;
- X lexan(&p_pc);
- X }
- X /* p_pc now points to just after ENDIF */
- X }
- X break;
- X case WHILE:
- X if (nest_parse > PARSNEST){
- X sprintf(Msg,"Nesting level too deep");
- X S_abort();
- X }
- X S_WHILE = p_pc;
- X S_DO = S_DONE = NIL(char);
- X while ((w_status=S_parse(S_WHILE,DO))==TRUE){
- X DNP;
- X --nest_while;
- X if (!S_DO)
- X lexan(&intercom),
- X S_DO = intercom;
- X status = S_parse(S_DO,DONE);
- X DNP;
- X --nest_while;
- X if (status == SBREAK){
- X status = 0;
- X break;
- X } /* note SCONTNUE is automatic */
- X if (!S_DONE && status!=SCONTNUE)
- X lexan(&intercom),
- X S_DONE = intercom;
- X }
- X if (S_DONE)
- X p_pc = S_DONE;
- X else {
- X cptr = S_DO ? S_DO : S_WHILE;
- X while (TRUE){
- X switch ((nexttype=lexan(&cptr))){
- X case WHILE:
- X ++counter;
- X continue;
- X case DONE:
- X if (counter){
- X --counter;
- X continue;
- X }
- X p_pc = cptr;
- X break;
- X case NULLTOK:
- X S_abort();
- X default:
- X continue;
- X }
- X break;
- X }
- X /* p_pc now points to just after matching 'done' */
- X }
- X if (w_status==FALSE)
- X DNP;
- X break;
- X } /* end of main switch, whew */
- X if (t_invoke==TERMINAT)
- X return status;
- X n = -1;
- X }
- X}
- X
- X/* load a script and call S_parse to run it */
- static void
- S_call(scriptname)
- char *scriptname;
- X{
- X int i;
- X jmp_buf senv;
- X char *oldtvec, *newptr, *oldptr, script[SM_BUFF];
- X long filesize;
- X FILE *scriptfp;
- X static struct stat statbuf;
- X
- X strcpy(script, scriptname);
- X memset(Msg, 0, SM_BUFF);
- X if (++nest_cmd>CMDNEST){
- X S2("Too many nested scripts");
- X --nest_cmd;
- X return;
- X }
- X if (!(scriptfp = openfile(script))){
- X sprintf(Msg,"Can't open '%s'",script);
- X S2(Msg);
- X --nest_cmd;
- X return;
- X }
- X
- X /* this succeeds, openfile() has called isregfile() to stat the file */
- X fstat(fileno(scriptfp),&statbuf);
- X filesize = statbuf.st_size;
- X
- X areas[nest_cmd - 1] = NIL(char);
- X if (!(areas[nest_cmd-1]=(char*)calloc((unsigned)filesize+10,1))){
- X sprintf(Msg,"%s: allocation error",script);
- X S2(Msg);
- X fclose(scriptfp);
- X --nest_cmd;
- X return;
- X }
- X if (strcmp(STARTUP,script))
- X sprintf(Msg,"Running %s",script),
- X S2(Msg);
- X *(areas[nest_cmd - 1]) = '\n';
- X fread((areas[nest_cmd - 1] + 1),filesize,1,scriptfp);
- X *(areas[nest_cmd - 1] + filesize + 1) = '\0';
- X fclose(scriptfp);
- X
- X oldtvec = tvector;
- X tvector = NIL(char);
- X newptr = (char *)senv;
- X oldptr = (char *)env;
- X for (i=0; i<sizeof(env); ++i)
- X *(newptr++) = *(oldptr++);
- X if ((i=setjmp(env))==0)
- X S_parse(areas[nest_cmd - 1],NULLTOK);
- X else if (i==TTERROR)
- X S2("Abnormal script termination");
- X else if (i==TIMEOUT)
- X S2("Timeout");
- X else if (i==EXIT)
- X S2("Script encountered 'exit'");
- X
- X tvector = oldtvec;
- X newptr = (char *)env;
- X oldptr = (char *)senv;
- X for (i=0; i<sizeof(env); ++i)
- X *(newptr++) = *(oldptr++);
- X --nest_cmd;
- X if (areas[nest_cmd])
- X free(areas[nest_cmd]);
- X
- X if (strcmp(STARTUP,script))
- X sprintf(Msg,"%s COMPLETE",script),
- X S2(Msg);
- X}
- X
- static
- S_abort()
- X{
- X char *cptr;
- X
- X if (*Msg)
- X S2(Msg);
- X
- X cptr = intercom;
- X while (*cptr && *cptr!='\n')
- X --cptr;
- X
- X ++cptr;
- X while (*cptr && *cptr!='\n')
- X fputc(*(cptr++),tfp);
- X fputc('\r',tfp),
- X fputc('\n',tfp);
- X
- X if (tfp==cfp)
- X tfp = savetfp;
- X unsetall();
- X longjmp(env,TTERROR);
- X}
- X
- X/* get_bound_char() get a character from the user's keyboard. If the terminal
- X mode escape character is seen, look ahead to the next character and act on
- X it. If unrecognized, simply swallow the escape character and return the
- X second character. Returns ASCII character code, or XCBIND function code
- X*/
- get_bound_char()
- X{
- X int c, lc;
- X binding_t *ptr;
- X static char *emit_string = NIL(char);
- X
- X if (emit_string) {
- X c = *(emit_string++);
- X if (!c)
- X emit_string = NIL(char);
- X }
- X
- X if (!emit_string) {
- X c = getchar();
- X if (c == my_escape) {
- X lc = tolower(c=getchar());
- X for (ptr = first_binding; ptr; ptr = ptr->bs_next){
- X if (ptr->bs_c == lc){
- X switch (ptr->bs_function){
- X case EMITSTR:
- X emit_string = ptr->bs_string;
- X return *(emit_string++);
- X case DOSCRPT:
- X strcpy(ddsname, ptr->bs_string);
- X return DOSCRPT;
- X default:
- X return ptr->bs_function;
- X }
- X }
- X }
- X }
- X }
- X return c;
- X}
- X
- X/* default_bindings restores XC to its default key bindings.
- X Uppercase keys are mapped to lower case.
- X*/
- void
- default_bindings()
- X{
- X bind_key('/', HLPCHAR, "");
- X bind_key('?', HLPCHAR, "");
- X bind_key('b', BRKCHAR, "");
- X bind_key('d', DIALCHR, "");
- X bind_key('f', DIVCHAR, "");
- X bind_key('h', HUPCHAR, "");
- X bind_key('n', CAPTEND, "");
- X bind_key('q', QUITCHR, "");
- X bind_key('s', SCRPCHR, "");
- X bind_key('x', ENDCHAR, "");
- X bind_key('y', CAPTYES, "");
- X}
- X
- X/* show_emit() performs an unctrl() operation on an entire buffer. */
- static void
- show_emit(str)
- char *str;
- X{
- X while (*str)
- X fprintf(tfp,"%s", unctrl(*str++));
- X}
- X
- X/* get_function() returns the description corresponding to the function code
- X specified by (code).
- X*/
- static char *
- get_function(code)
- int code;
- X{
- X bindstr_t *ptr = function_list;
- X int i;
- X
- X for (i = 0; i < FUNCTION_COUNT; i++, ptr++)
- X if ((int) ptr->bf_function == code)
- X return ptr->bf_string;
- X
- X return "???";
- X}
- X
- show_bindings()
- X{
- X binding_t *ptr = first_binding;
- X char *escape_str = strdup(unctrl(my_escape));
- X int curline = 0;
- X
- X cls();
- X
- X fprintf(tfp,"\tTERMINAL mode escape character is %s.\r\n\r\n", escape_str);
- X curline += 2;
- X
- X while (ptr){
- X if (curline >= LI - 2){
- X S0("PRESS ENTER");
- X getline();
- X cls();
- X curline = 0;
- X }
- X
- X fprintf(tfp,"\t%s - %-3.3s %s", escape_str, unctrl(ptr->bs_c),
- X get_function(ptr->bs_function));
- X
- X switch (ptr->bs_function){
- X case EMITSTR:
- X case DOSCRPT:
- X fputc(' ',tfp);
- X fputc('"',tfp);
- X show_emit(ptr->bs_string);
- X fputc('"',tfp);
- X break;
- X }
- X fputc('\r',tfp);
- X fputc('\n',tfp);
- X curline++;
- X
- X ptr = ptr->bs_next;
- X }
- X
- X free(escape_str);
- X}
- X
- X/* find_function() returns the function code referenced by the name
- X pointed to by (name). Returns BADFUNC if the name is unrecognized.
- X*/
- static
- bindfunc_t find_function(name)
- char *name;
- X{
- X bindstr_t *ptr = function_list;
- X int i;
- X
- X for (i = 0; i < FUNCTION_COUNT; i++, ptr++)
- X if (strcmp(ptr->bf_name, name) == 0)
- X return ptr->bf_function;
- X
- X return BADFUNC;
- X}
- END_OF_FILE
- if test 44712 -ne `wc -c <'xcscrpt.c'`; then
- echo shar: \"'xcscrpt.c'\" unpacked with wrong size!
- fi
- # end of 'xcscrpt.c'
- fi
- echo shar: End of archive 3 \(of 3\).
- cp /dev/null ark3isdone
- MISSING=""
- for I in 1 2 3 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 3 archives.
- rm -f ark[1-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
-